<repo_name>fbergmann/edit-spatial<file_sep>/LibEditSpatial/Model/DebugConfig.cs
using System.Collections.Generic;

namespace LibEditSpatial.Model
{
  public class DebugConfig
  {
    public int Verbosity { get; set; }
    public int Instationary { get; set; }

    public static DebugConfig FromDict(Dictionary<string, string> dict)
    {
      var result = new DebugConfig
      {
        Verbosity = dict.Get<int>("verbosity"),
        Instationary = dict.Get<int>("Instationary")
      };
      return result;
    }

    public Dictionary<string, string> ToDict()
    {
      var result = new Dictionary<string, string>();
      result["verbosity"] = Verbosity.ToString();
      result["Instationary"] = Instationary.ToString();
      return result;
    }
  }
}<file_sep>/LibEditSpatial/Controls/CtrlPalette.cs
﻿using System;
using System.Globalization;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Controls
{
  public partial class CtrlPalette : UserControl
  {
    private DmpPalette _Palette;

    public CtrlPalette()
    {
      InitializeComponent();
      Current = new PalleteArgs();
      IssuingEvents = true;
      IsPainting = false;
    }

    public PalleteArgs Current { get; set; }

    public bool IssuingEvents { get; set; }
    
    public bool IsPainting { get; set; }

    public DmpPalette Palette
    {
      get
      {
        if (_Palette == null)
          _Palette = DmpPalette.Default;
        return _Palette;
      }

      set
      {
        _Palette = value;
        pictureBox1.Image = _Palette.ToImage(pictureBox1.Size);
      }
    }

    public event EventHandler<PalleteArgs> PalleteValueChanged;
    public event EventHandler<DmpPalette> PalleteChanged;

    public void UpdateValues(double min, double current, double max)
    {
      if (current > max) current = max;
      if (current < min) current = min;

      IssuingEvents = false;
      txtMin.Text = min.ToString(CultureInfo.InvariantCulture);
      txtCurrent.Text = current.ToString(CultureInfo.InvariantCulture);
      txtMax.Text = max.ToString(CultureInfo.InvariantCulture);
      trackBar1.Minimum = (int) (min*100f);
      trackBar1.Maximum = (int) (max*100f);
      trackBar1.Value = (int) (current*100f);
      Current.Min = min;
      Current.Value = current;
      Current.Max = max;
      IssuingEvents = true;
    }

    private void OnPaletteChanged()
    {
      if (PalleteChanged != null && IssuingEvents)
        PalleteChanged(this, Palette);
    }

    private void OnPaletteValueChanged()
    {
      if (PalleteValueChanged != null && IssuingEvents)
        PalleteValueChanged(this, Current);
    }

    private void OnMinChanged(object sender, EventArgs e)
    {
      double val;
      if (double.TryParse(txtMin.Text, out val))
      {
        Current.Min = val;
        trackBar1.Minimum = (int) (val*100);
        OnPaletteValueChanged();
      }
    }

    private void SetCurrent(double val)
    {
      Current.Value = val;
      trackBar1.Value = (int) Math.Min((Math.Max(val, trackBar1.Minimum)*100), trackBar1.Maximum);
      OnPaletteValueChanged();
    }

    private void OnCurrentChanged(object sender, EventArgs e)
    {
      double val;
      if (double.TryParse(txtCurrent.Text, out val))
      {
        SetCurrent(val);
      }
    }

    private void OnScrollChanged(object sender, EventArgs e)
    {
      txtCurrent.Text = ((double) (trackBar1.Value)/100f).ToString(CultureInfo.InvariantCulture);
    }

    private void OnMaxChanged(object sender, EventArgs e)
    {
      double val;
      if (double.TryParse(txtMax.Text, out val))
      {
        Current.Max = val;
        trackBar1.Maximum = (int) (val*100);
        OnPaletteValueChanged();
      }
    }

    private void OnMouseUp(object sender, MouseEventArgs e)
    {
      IsPainting = false;
    }

    private void OnMouseDown(object sender, MouseEventArgs e)
    {
      IsPainting = true;
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
      if (Palette == null || !IsPainting) return;

      var length = pictureBox1.Width/(float) Palette.Colors.Count;
      float test = (int) (e.X/length);
      if (test >= Palette.Colors.Count)
        test = Palette.Colors.Count - 1;

      var stretchX = Math.Round(Current.MapFromUnit(test / Palette.Colors.Count), 2);
      SetCurrent(stretchX);
      txtCurrent.Text = stretchX.ToString(CultureInfo.InvariantCulture);
      //
    }

    private void OnMouseLeave(object sender, EventArgs e)
    {
      IsPainting = false;
    }

    private void OnMouseClick(object sender, MouseEventArgs e)
    {
      if (Palette == null) return;

      var length = pictureBox1.Width/(float) Palette.Colors.Count;
      float test = (int) (e.X/length);
      if (test >= Palette.Colors.Count)
        test = Palette.Colors.Count - 1;

      var stretchX = Math.Round(Current.MapFromUnit(test / Palette.Colors.Count), 2);
      SetCurrent(stretchX);
      txtCurrent.Text = stretchX.ToString(CultureInfo.InvariantCulture);
    }

    public void ChangePalette(DmpPalette palette)
    {
      Palette = palette;
      OnPaletteChanged();
    }

    public void ChangePalette(string filename)
    {
      var palette = DmpPalette.FromFile(filename);
      ChangePalette(palette);
    }

    private void OnLoadPalette(object sender, EventArgs e)
    {
      using (var dialog = new OpenFileDialog
      {
        Title = "Open Palette",
        Filter = "Palette files|*.txt|All files|*.*",
        AutoUpgradeEnabled = true
      })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
        {
          ChangePalette(dialog.FileName);
        }
      }
    }
  }
}<file_sep>/LibEditSpatial/Util.cs
﻿using System.Windows.Forms;
using Ookii.Dialogs;

namespace LibEditSpatial
{
  public static class Util
  {
    public static string ToCygwin(string dir)
    {
      dir = dir.Replace("\\", "/");
      var separator = dir.IndexOf(':');
      if (separator == -1) return dir;
      var drive = dir.Substring(0, separator);
      var sub = dir.Substring(separator + 1);
      return string.Format("/cygdrive/{0}{1}", drive, sub);
    }

    public static string GetDir(string baseDir)
    {
      using (var dlg = new VistaFolderBrowserDialog { SelectedPath = baseDir, Description = "Open Directory" })
      {
        if (dlg.ShowDialog() == DialogResult.OK)
          return dlg.SelectedPath;
      }
      return baseDir;
    }
  }
}<file_sep>/LibEditSpatial/Model/VoidByteStringDelegate.cs
namespace LibEditSpatial.Model
{
  public delegate void VoidByteStringDelegate(byte[] data, string species);
}<file_sep>/LibEditSpatial/Model/DuneConfig.cs
﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using libsbmlcs;

namespace LibEditSpatial.Model
{
  public class DuneConfig
  {
    public bool Dirty { get; set; }

    internal const string STR_GLOBAL = "Global";
    internal DomainConfig _DomainConfig;
    internal GlobalConfig _GlobalConfig;
    internal NewtonConfig _NewtonConfig;
    internal TimeLoopConfig _TimeConfig;

    public Dictionary<string, string> VariableCompartmentMap { get; set; }
    public List<string> CompartmentIds { get; set; }


    /// <summary>
    ///   Initializes a new instance of the DuneConfig class.
    /// </summary>
    public DuneConfig()
    {
      Entries = new Dictionary<string, Dictionary<string, string>>();
      VariableCompartmentMap = new Dictionary<string, string>();
      CompartmentIds = new List<string>();

    }

    public string FileName { get; set; }
    public Dictionary<string, Dictionary<string, string>> Entries { get; set; }

    public GlobalConfig GlobalConfig
    {
      get
      {
        if (_GlobalConfig == null)
        {
          if (Entries.ContainsKey(STR_GLOBAL))
            _GlobalConfig = GlobalConfig.FromDict(Entries[STR_GLOBAL]);
          else
            _GlobalConfig = GlobalConfig.Default;
        }

        return _GlobalConfig;
      }
      set
      {
        Dirty = true;
        _GlobalConfig = value;
        Entries.Remove(STR_GLOBAL);
        Entries.Add(STR_GLOBAL, value.ToDict());
      }
    }

    public NewtonConfig NewtonConfig
    {
      get
      {
        if (_NewtonConfig == null)
        {
          if (Entries.ContainsKey("Newton"))
            _NewtonConfig = NewtonConfig.FromDict(Entries["Newton"]);
          else
            _NewtonConfig = NewtonConfig.Default;
        }

        return _NewtonConfig;
      }
      set
      {
        Dirty = true;
        _NewtonConfig = value;
        Entries.Remove("Newton");
        Entries.Add("Newton", value.ToDict());
      }
    }

    public DomainConfig DomainConfig
    {
      get
      {
        if (_DomainConfig == null)
        {
          if (Entries.ContainsKey("Domain"))
            _DomainConfig = DomainConfig.FromDict(Entries["Domain"]);
          else
            _DomainConfig = DomainConfig.Default;
        }

        return _DomainConfig;
      }
      set
      {
        Dirty = true;
        _DomainConfig = value;
        Entries.Remove("Domain");
        Entries.Add("Domain", value.ToDict());
      }
    }

    public TimeLoopConfig TimeConfig
    {
      get
      {
        if (_TimeConfig == null)
        {
          if (Entries.ContainsKey("Timeloop"))
            _TimeConfig = TimeLoopConfig.FromDict(Entries["Timeloop"]);
          else
            _TimeConfig = TimeLoopConfig.Default;
        }

        return _TimeConfig;
      }
      set
      {
        Dirty = true;
        _TimeConfig = value;
        Entries.Remove("Timeloop");
        Entries.Add("Timeloop", value.ToDict());
      }
    }

    public Dictionary<string, string> this[string key]
    {
      get
      {
        if (!Entries.ContainsKey(key))
          Entries[key] = new Dictionary<string, string>();
        return Entries[key];
      }
    }

    /// <summary>
    /// Returns all species in the given compartment
    /// </summary>
    /// <param name="compId">the compartment id</param>
    /// <returns>all species in that compartment</returns>
    public List<string> GetSpeciesIn(string compId)
    {
      return VariableCompartmentMap.Where(item => item.Value == compId).Select(item2 => item2.Key).ToList();
    }

    /// <summary>
    /// Initialize SBML mapping of species from the given SBML file
    /// </summary>
    /// <param name="fileName">the sbml file</param>
    public void InitFromSBML(string fileName)
    {
      CompartmentIds = new List<string>();
      VariableCompartmentMap = new Dictionary<string, string>();

      var doc = libsbml.readSBMLFromFile(fileName);
      var model = doc.getModel();
      if (model == null) return;

      CompartmentIds = new List<string>();
      VariableCompartmentMap = new Dictionary<string, string>();

      for (var i = 0; i < model.getNumCompartments(); ++i)
        CompartmentIds.Add(model.getCompartment(i).getId());

      for (var i = 0; i < model.getNumSpecies(); ++i)
      {
        var species = model.getSpecies(i);
        VariableCompartmentMap[species.getId()] = species.getCompartment();
      }
      
    }

    /// <summary>
    /// Return the name of all entries that represent variables
    /// </summary>
    /// <returns></returns>
    public List<string> GetVariableKeys()
    {
      var result = new List<string>(Entries.Keys);
      result.Remove(STR_GLOBAL);
      result.Remove("Timeloop");
      result.Remove("Verbosity");
      result.Remove("Newton");
      result.Remove("Data");
      result.Remove("Reaction");
      result.Remove("Domain");
      return result;
    }

    private void WriteSection(StringBuilder builder, string key, Dictionary<string, string> map, string basedir = null)
    {
      if (!string.IsNullOrWhiteSpace(key))
        builder.AppendFormat("[{0}]{1}", key, Environment.NewLine);

      foreach (var item in map)
      {        
        var val = item.Value ==null ? string.Empty : item.Value.Trim();
        if (!string.IsNullOrWhiteSpace(basedir) && val.StartsWith(basedir))
          val = val.Replace(basedir, "");

        builder.AppendFormat("{0} = {1}{2}", item.Key, val, Environment.NewLine);
      }

      builder.AppendLine();
    }

    public void SaveAs(string fileName)
    {
      var baseDir = Path.GetDirectoryName(fileName);

      var builder = new StringBuilder();
      builder.AppendLine("# Dune Config file");
      builder.AppendLine(string.Format("# written: {0} {1}", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString()));
      builder.AppendLine();

      WriteSection(builder, "", GlobalConfig.ToDict(), baseDir);
      WriteSection(builder, "Newton", NewtonConfig.ToDict(), baseDir);
      if (!Entries.ContainsKey("Verbosity"))
      {
        var tmp = new Dictionary<string, string>();
        tmp["verbosity"] = "0";
        tmp["Instationary"] = "0";
        Entries.Add("Verbosity", tmp);
      }

      WriteSection(builder, "Verbosity", Entries["Verbosity"], baseDir);
      WriteSection(builder, "Timeloop", TimeConfig.ToDict(), baseDir);
      WriteSection(builder, "Domain", DomainConfig.ToDict(), baseDir);
      WriteSection(builder, "Reaction", Entries["Reaction"], baseDir);
      foreach (var item in GetVariableKeys())
        WriteSection(builder, item, Entries[item], baseDir);
      if (Entries.ContainsKey("Data"))
        WriteSection(builder, "Data", Entries["Data"], baseDir);

      File.WriteAllText(fileName, builder.ToString());

      FileName = fileName;
      Dirty = false;
      
    }

    public void ApplyCompartmentMasking(string compId, string fileName)
    {
      var ids = GetSpeciesIn(compId);
      var variables = GetVariableKeys();
      var main = this["Reaction"];

      foreach (var id in ids)
      {
        if (!variables.Contains(id)) continue;
        var current = this[id];
        if (current == null) continue;
        current["file_compartment"] = fileName;
      }

      if (main == null) return;

      main["in_" + compId] = fileName;
      Dirty = true;
    }


    public static DuneConfig FromFile(string filename)
    {
      var lines = File.ReadAllLines(filename);

      var result = new DuneConfig {
        FileName = filename,
        VariableCompartmentMap = new Dictionary<string, string>(),
        CompartmentIds = new List<string>()

      };

      var current = STR_GLOBAL;
      var entries = new Dictionary<string, string>();

      foreach (var line in lines)
      {
        // skip comments
        var trimmed = line.Trim();

        // remove comment at the end
        var commentIndex = trimmed.IndexOf("#", StringComparison.Ordinal);
        if (commentIndex != -1)
        {
          trimmed = trimmed.Substring(0, commentIndex).Trim();
        }

        if (trimmed.StartsWith("#")) continue;

        if (trimmed.StartsWith("[") && trimmed.EndsWith("]"))
        {
          result.Entries.Add(current, entries);
          current = trimmed.Replace("[", "").Replace("]", "");
          entries = new Dictionary<string, string>();
          continue;
        }

        var pair = trimmed.Split(new[] {'='}, StringSplitOptions.RemoveEmptyEntries);
        if (pair.Length > 0)
        {
          var key = pair[0].Trim();
          if (!string.IsNullOrWhiteSpace(key))
            if (pair.Length >= 2)
              entries[key] = pair[1].Trim();
            else
              entries[key] = "";
        }
      }

      result.Entries.Add(current, entries);

      result.InitCompartmentMasking();


      return result;
    }

    public void ClearCompartmentMasking()
    {
      var variables = GetVariableKeys();
      foreach (var id in variables)
      {
        var current = this[id];
        if (current.ContainsKey("file_compartment"))
          current.Remove("file_compartment");
      }

      var main = this["Reaction"];
      if (main == null) return;

      var keys = new List<string>(main.Keys);
      foreach (var key in keys)
      {
        if (key.StartsWith("in_"))
          main.Remove(key);
      }
      Dirty = true;

    }
    public void InitCompartmentMasking()
    {

      var sbmlFile = GlobalConfig.SBMLFile;

      if (!string.IsNullOrWhiteSpace(sbmlFile))
      {
        var currentDir = Path.GetDirectoryName(FileName);
        if (File.Exists(sbmlFile))
        {
          InitFromSBML(sbmlFile);
        }

        else if (File.Exists(Path.Combine(currentDir, sbmlFile)))
        {
          InitFromSBML(Path.Combine(currentDir, sbmlFile));
        }
        return;
      }

    }
  }
}<file_sep>/WFEditSpatial/Templates/main.cc
// -*- tab-width: 4; indent-tabs-mode: nil -*-
/** \file

    \brief Solve two-component diffusion-reaction system
    with finite volume scheme. This class tests the
    multi domain grid.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <stdlib.h>

#include <dune/common/parallel/mpihelper.hh>
#include <dune/common/exceptions.hh>
#include <dune/common/fvector.hh>
#include <dune/common/static_assert.hh>
#include <dune/common/timer.hh>
#include <dune/common/parametertreeparser.hh>
#include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh>
#include <dune/grid/io/file/gmshreader.hh>
#include <dune/grid/yaspgrid.hh>
#include <dune/grid/multidomaingrid/indexsets.hh>
#include <dune/grid/multidomaingrid/subdomainset.hh>
#include <dune/grid/multidomaingrid.hh>
#include <dune/grid/common/gridinfo.hh>

#if HAVE_ALBERTA
#include <dune/grid/albertagrid.hh>
#include <dune/grid/albertagrid/dgfparser.hh>
#endif

#if HAVE_UG
#include <dune/grid/uggrid.hh>
#endif

#if HAVE_ALUGRID
#include <dune/grid/alugrid.hh>
#include <dune/grid/io/file/dgfparser/dgfalu.hh>
#include <dune/grid/io/file/dgfparser/dgfparser.hh>
#endif

#include <dune/istl/bvector.hh>
#include <dune/istl/operators.hh>
#include <dune/istl/solvers.hh>
#include <dune/istl/preconditioners.hh>
#include <dune/istl/io.hh>
#include <dune/istl/superlu.hh>

#include <dune/copasi/utilities/newton.hh>
#include <dune/copasi/utilities/newtonutilities.hh>
#include <dune/copasi/utilities/timemanager.hh>
#include <dune/copasi/utilities/datahelper.hh>
#include <dune/copasi/utilities/sbmlhelper.hh>
#include <dune/copasi/utilities/solutioncontrol.hh>

#include <dune/pdelab/common/function.hh>
#include <dune/pdelab/common/vtkexport.hh>
#include <dune/pdelab/instationary/pvdwriter.hh>

#include <dune/pdelab/backend/istl/tags.hh>
#include <dune/pdelab/backend/istl/descriptors.hh>
#include <dune/pdelab/backend/istl/bcrsmatrixbackend.hh>

#include <dune/pdelab/backend/istlvectorbackend.hh>
#include <dune/pdelab/backend/istlmatrixbackend.hh>
#include <dune/pdelab/backend/istlsolverbackend.hh>
#include <dune/pdelab/backend/seqistlsolverbackend.hh>

#include <dune/pdelab/constraints/conforming.hh>
#include <dune/pdelab/constraints/constraints.hh>
#include <dune/pdelab/ordering/orderingbase.hh>

#include <dune/pdelab/finiteelementmap/p0fem.hh>
#include <dune/pdelab/constraints/p0.hh>
#include <dune/pdelab/constraints/constraints.hh>
#include <dune/pdelab/gridoperator/gridoperator.hh>
#include <dune/pdelab/gridoperator/onestep.hh>
#include <dune/pdelab/stationary/linearproblem.hh>
#include <dune/pdelab/instationary/onestep.hh>
#include <dune/pdelab/gridfunctionspace/subspace.hh>

#include <dune/pdelab/multidomain/multidomaingridfunctionspace.hh>
#include <dune/pdelab/multidomain/coupling.hh>
#include <dune/pdelab/multidomain/subproblemlocalfunctionspace.hh>
#include <dune/pdelab/multidomain/gridoperator.hh>
#include <dune/pdelab/multidomain/subproblem.hh>
#include <dune/pdelab/constraints/conforming.hh>
#include <dune/pdelab/multidomain/constraints.hh>
#include <dune/pdelab/multidomain/interpolate.hh>
#include <dune/pdelab/multidomain/vtk.hh>

#include "componentparameters.hh"
#include "local_operator.hh"
#include "initial_conditions.hh"
#include "reactionadapter.hh"

#include <dune/copasi/utilities/solutiontimeoutput.hh>

/**
 * global data helper variable, that can be used to exchange the geometry
 * via config file
 */

DataHelper* dh_geometry = NULL;
double geometry_min = 10;
double geometry_max = 10;


EVENT_SUPPORT_GLOBALS(mEvents, skipOutputUntilEvent, appliedEvent);

/** \brief Control time step after reaction.

    If some concentration is negative, then it returns false.
    Otherwise it returns true

    \tparam V           Vector backend (containing DOF)
*/
template<typename V>
bool controlReactionTimeStep (V &v)
{
    for (auto it=v.begin(); it!=v.end();++it)
        if (*it<0) return false;

    return true;
}

template<class GV>
void run (const GV& gv, Dune::ParameterTree & param)
{
    const bool verbosity = param.sub("Verbosity").get<bool>("verbosity", false);
    if (verbosity)
    Dune::gridinfo(gv.grid());
    typedef typename GV::Grid::ctype DF;
    typedef double RF;
    const int dim = GV::dimension;
    Dune::Timer watch;

    std::string timestepping = param.get<std::string>("timestepping");


    // output to vtk file (true/false)
    const bool graphics = param.get<bool>("writeVTK", false);

    ConvergenceAdaptiveTimeStepper<RF> timemanager(param);

    // for each component parameters different classes
    typedef DiffusionParameter<GV,RF> DP;
%DPCREATION%

    typedef Dune::PDELab::MulticomponentDiffusion<GV,RF,DP,%NUMCOMPONENTS%> VCT;
    VCT vct(%DPINITIALIZATION%);

    // <<<2>>> Make grid function space
    typedef Dune::PDELab::P0LocalFiniteElementMap<DF,RF,dim> FEM;
    FEM fem(Dune::GeometryType(Dune::GeometryType::cube,dim));
    typedef Dune::PDELab::P0ParallelConstraints CON;
    typedef Dune::PDELab::ISTLVectorBackend<> VBE0;
    typedef Dune::PDELab::GridFunctionSpace<GV,FEM,CON,VBE0> GFS;

    typedef Dune::PDELab::ISTLVectorBackend<> VBE;
            //<Dune::PDELab::ISTLParameters::static_blocking,VCT::COMPONENTS> VBE;
    typedef Dune::PDELab::PowerGridFunctionSpace<GFS,VCT::COMPONENTS,VBE,
            Dune::PDELab::EntityBlockedOrderingTag> TPGFS;
    watch.reset();
    CON con;
    GFS gfs(gv,fem,con);
    TPGFS tpgfs(gfs);
    if (verbosity) std::cout << "=== function space setup " <<  watch.elapsed() << " s" << std::endl;

    // some informations
    tpgfs.update();
    if (verbosity) std::cout << "number of DOF =" << tpgfs.globalSize() << std::endl;

    watch.reset();


    // <<<2b>>> make subspaces for visualization
%SUBCREATION%


   if (verbosity) std::cout << "=== grid function subspaces " << watch.elapsed() << " s" << std::endl;
   watch.reset();

    // make constraints map and initialize it from a function (boundary conditions)
    typedef typename TPGFS::template ConstraintsContainer<RF>::Type CC;
    CC cg;
    cg.clear();
    Dune::PDELab::constraints(tpgfs,cg,false);

    typedef ReactionAdapter<RF> RA;
    RA ra(param);

    typedef Dune::PDELab::MulticomponentCCFVSpatialDiffusionOperator<VCT,RA> LOP;
    LOP lop(vct,ra); //local operator including reaction adapter
    typedef Dune::PDELab::MulticomponentCCFVTemporalOperator<VCT> TLOP;
    TLOP tlop(vct);
    typedef Dune::PDELab::istl::BCRSMatrixBackend<> MBE;
    MBE mbe(27); // Maximal number of nonzeroes per row can be cross-checked by patternStatistics().
    // grid operators
    typedef Dune::PDELab::GridOperator<TPGFS,TPGFS,LOP,MBE,RF,RF,RF,CC,CC> CGO0;
    CGO0 cgo0(tpgfs,cg,tpgfs,cg,lop,mbe);
    typedef Dune::PDELab::GridOperator<TPGFS,TPGFS,TLOP,MBE,RF,RF,RF,CC,CC> CGO1;
    CGO1 cgo1(tpgfs,cg,tpgfs,cg,tlop,mbe);
    // one step grid operator for instationary problems


    typedef Dune::PDELab::OneStepGridOperator<CGO0, CGO1, false> EIGO;
    typedef Dune::PDELab::OneStepGridOperator<CGO0, CGO1, true> DIGO;

    DIGO digo(cgo0, cgo1);
    EIGO eigo(cgo0, cgo1);


    // <<<7>>> make vector for old time step and initialize
    typedef typename Dune::PDELab::BackendVectorSelector<TPGFS, RF>::Type V;
    V uold(tpgfs,0.0);
    V unew(tpgfs,0.0);

    // initial conditions
    typedef UGeneralInitial<GV, RF> UGeneralInitialType;
%INITIALTYPECREATION%


    typedef Dune::PDELab::PowerGridFunction<UGeneralInitialType, %NUMCOMPONENTS%> UInitialType;
    UInitialType uinitial(%INITIALARGS%); //new

    //typedef Dune::PDELab::CompositeGridFunction<%INITIALTYPES%> UInitialType; //new
    //UInitialType uinitial(%INITIALARGS%); //new
    Dune::PDELab::interpolate(uinitial,tpgfs,uold);
    unew = uold;
    
    if (verbosity) std::cout << "=== initial conditions " << watch.elapsed() << " s" << std::endl;
  // <<<5>>> Select a linear solver backend
#if HAVE_MPI
#if HAVE_SUPERLU
    typedef Dune::PDELab::ISTLBackend_BCGS_AMG_SSOR<DIGO> DLS;
    DLS dls(tpgfs, param.sub("Newton").get<int>("LSMaxIterations", 100), param.sub("Newton").get<int>("LinearVerbosity", 0));
#else
    typedef Dune::PDELab::ISTLBackend_OVLP_BCGS_SSORk<GFS, CC> DLS;
    DLS dls(tpgfs, cc, 5000, 5, param.sub("Newton").get<int>("LinearVerbosity", 0));
#endif
#else //!parallel
#if HAVE_SUPERLU
    typedef Dune::PDELab::ISTLBackend_SEQ_SuperLU DLS;
    DLS dls(param.sub("Newton").get<int>("LinearVerbosity", 0));
#else
    typedef Dune::PDELab::ISTLBackend_SEQ_BCGS_SSOR DLS;
    DLS dls(5000, param.sub("Newton").get<int>("LinearVerbosity", 0));
#endif
#endif

    typedef Dune::PDELab::ISTLBackend_OVLP_ExplicitDiagonal<TPGFS> ELS;
    ELS els(tpgfs);

    if (verbosity) std::cout << "=== linear solvers " << watch.elapsed() << " s" << std::endl;
    // <<<6>>> Solver for non-linear problem per stage
    typedef Dune::PDELab::Newton<DIGO, DLS, V> CPDESOLVER;
    CPDESOLVER pdesolver(digo, dls);
    pdesolver.setLineSearchStrategy(CPDESOLVER::hackbuschReuskenAcceptBest); //strategy for linesearch
    typedef Dune::PDELab::NewtonParameters NewtonParameters;
    NewtonParameters newtonparameters(param.sub("Newton"));
    newtonparameters.set(pdesolver);
    typedef Dune::PDELab::OneStepMethod<RF, DIGO, CPDESOLVER, V, V> OSMI;
    Dune::PDELab::Alexander2Parameter<RF> imethod;
    OSMI osmi(imethod, digo, pdesolver);

    // <<<7>>> time-stepper
    typedef Dune::PDELab::SimpleTimeController<RF> TC;
    TC tc;

    typedef Dune::PDELab::ExplicitOneStepMethod<RF, EIGO, ELS, V, V, TC> OSME;
    Dune::PDELab::ExplicitEulerParameter<RF> emethod;
    OSME osme(emethod, eigo, els, tc);

    if (verbosity) std::cout << "=== osm " << watch.elapsed() << " s" << std::endl;

    Dune::PDELab::TimeSteppingMethods<RF> timesteppingmethods;
    timesteppingmethods.setTimestepMethod(osme, param.get<std::string>("explicitsolver"));
    timesteppingmethods.setTimestepMethod(osmi, param.get<std::string>("implicitsolver"));

    osme.setVerbosityLevel(param.sub("Verbosity").get<int>("Instationary", 0));
    osmi.setVerbosityLevel(param.sub("Verbosity").get<int>("Instationary", 0));

    char basename[255];
    sprintf(basename,"%s-%01d",param.get<std::string>("VTKname","").c_str(),param.sub("Domain").get<int>("refine"));

    typedef Dune::PVDWriter<GV> PVDWriter;
    PVDWriter pvdwriter(gv,basename,Dune::VTK::conforming);
    // discrete grid functions
%CREATEGRIDFUNCTIONS%

    if (verbosity) std::cout << "=== output " << watch.elapsed() << " s" << std::endl;

    // <<<9>>> time loop

    if (graphics)
        pvdwriter.write(0);

    if (verbosity) std::cout << "=== output0 " << watch.elapsed() << " s" << std::endl;

    EVENT_SUPPORT_INITIALIZE(gv);

    double dt = timemanager.getTimeStepSize();
    while (!timemanager.finalize())
    {

        dt = timemanager.getTimeStepSize();
        if (gv.comm().rank() == 0)
        {
            if (verbosity)
            {
                std::cout << "======= solve reaction problem ======= from " << timemanager.getTime() << "\n";
                std::cout << "time " << timemanager.getTime() << " dt " << timemanager.getTimeStepSize() << std::endl;
            }           
        }

        watch.reset();
        try
        {
            // do time step
            if (timestepping == "explicit")
              osme.apply(timemanager.getTime(), dt, uold, unew);
            else
              osmi.apply(timemanager.getTime(), dt, uold, unew);

            if (!controlReactionTimeStep(unew))
            {
                timemanager.notifyFailure();
                unew = uold;
                continue;

            }

            uold = unew;

            if (gv.comm().rank() == 0 && verbosity)
                std::cout << "... done\n";
        }
        // newton linear search error
        catch (Dune::PDELab::NewtonLineSearchError) {
            if (gv.comm().rank() == 0 && verbosity)
                std::cout << "Newton Linesearch Error" << std::endl;
            timemanager.notifyFailure();
            unew = uold;
            if (!verbosity)
            {
              std::cout << "x" << std::flush;
            }
            continue;
        }
        catch (Dune::PDELab::NewtonNotConverged) {
            if (gv.comm().rank() == 0 && verbosity)
                std::cout << "Newton Convergence Error" << std::endl;
            timemanager.notifyFailure();
            unew = uold;
            if (!verbosity)
            {
              std::cout << "x" << std::flush;
            }
            continue;
        }
        catch (Dune::PDELab::NewtonLinearSolverError) {
            if (gv.comm().rank() == 0 && verbosity )
                std::cout << "Newton Linear Solver Error" << std::endl;
            timemanager.notifyFailure();
            unew = uold;
            if (!verbosity)
            {
              std::cout << "x" << std::flush;
            }
            continue;
        }
        catch (Dune::ISTLError) {
            if (gv.comm().rank() == 0 && verbosity )
                std::cout << "ISTL Error" << std::endl;
            timemanager.notifyFailure();
            unew = uold;
            if (!verbosity)
            {
              std::cout << "x" << std::flush;
            }
            continue;
        }

        if (gv.comm().rank() == 0 && verbosity)
            std::cout << "... took : " << watch.elapsed() << " s" << std::endl;

        // notify success in this timestep
        timemanager.notifySuccess(5);

        EVENT_SUPPORT_HANDLE_EVENTS(timemanager.getTime(), %NUMCOMPONENTS%, mEvents, uold, unew);

        if (graphics && timemanager.isTimeForOutput())
        {
            if ((skipOutputUntilEvent && appliedEvent) || !skipOutputUntilEvent)  
            {
                pvdwriter.write(timemanager.getTime());
                if (!verbosity)
                    std::cout << "o" << std::flush;
            }
            else if (!verbosity)
            {
              std::cout << "n" << std::flush;
            }
        }
        else if (!verbosity)
        {
          std::cout << "." << std::flush;
        }
    }


    std::cout << std::endl;

}


bool isInside(const Dune::FieldVector<double, 2>& point, const Dune::FieldVector<double, 2>& dimension)
{
  const auto& x = point[0];
  const auto& y = point[1];
  const auto& width = dimension[0];
  const auto& height = dimension[1];

  if (dh_geometry != NULL)
  {
    double val = (*dh_geometry).get((double)x, (double)y);
    bool inside = val >= (geometry_min - 1e-5) && val <= (geometry_max + 1e-5);
    return inside;
  }

%GEOMETRY%

}

//===============================================================
// Main program with grid setup
//===============================================================

int main(int argc, char** argv)
{
    try{
        //Maybe initialize Mpi
        Dune::MPIHelper& helper = Dune::MPIHelper::instance(argc, argv);

        std::string configfile = argv[0]; configfile += ".conf";

        // parse cmd line parameters
        if (argc > 1 && argv[1][0] != '-')
            configfile = argv[1];
        for (int i = 1; i < argc; i++)
        {
            if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h")
            {
                if(helper.rank()==0)
                    std::cout << "usage: ./%NAME% <configfile> [OPTIONS]" << std::endl;
                return 0;
            }
        }

        // read parameters from file
        Dune::ParameterTree param;
        Dune::ParameterTreeParser parser;
        parser.readINITree(configfile, param);

        EVENT_SUPPORT_READ_DATA(mEvents, parser, "eventinfo.ini", skipOutputUntilEvent);

        int dim=param.sub("Domain").get<int>("dim", 2);

        dh_geometry = DataHelper::forFile(param.sub("Domain").template get<std::string>("geometry", ""));
        geometry_min = param.sub("Domain").template get<double>("geometry_min", 0);
        geometry_max = param.sub("Domain").template get<double>("geometry_max", 0);

        if (dim==2)
        {
            // make grid
            Dune::FieldVector<double,2> L;
            L[0] = param.get<double>("Domain.height");
            L[1] = param.get<double>("Domain.width");

            std::array<int,2> N;
            N[0] = param.get<int>("Domain.nx");
            N[1] = param.get<int>("Domain.ny");

            std::bitset<2> periodic(false);
            int overlap = param.get<int>("overlap", 0);

            typedef Dune::YaspGrid<2> HostGrid;
            HostGrid hostgrid(helper.getCommunicator(),L,N,periodic,overlap);
            hostgrid.globalRefine(param.get<int>("Domain.refine",0));

            typedef Dune::MultiDomainGrid<HostGrid,Dune::mdgrid::ArrayBasedTraits<2,8,8> > MDGrid;
            MDGrid mdgrid(hostgrid,true);
            typedef typename MDGrid::LeafGridView MDGV;
            typedef typename MDGV::template Codim<0>::Iterator Iterator;
            typedef typename MDGrid::SubDomainIndexType SubDomainIndexType;

            MDGV mdgv = mdgrid.leafGridView();
            mdgrid.startSubDomainMarking();
            for(Iterator it = mdgv.template begin<0>(); it != mdgv.template end<0>(); ++it)
            {
                SubDomainIndexType subdomain = 0;
                mdgrid.removeFromAllSubDomains(*it);

                // figure out, whether it is part of the
                // region, and if so add it like so:

                const auto& center = it->geometry().center();
                if (isInside(center, L))
                {
                    mdgrid.addToSubDomain(subdomain,*it);
                }
            }

            mdgrid.preUpdateSubDomains();
            mdgrid.updateSubDomains();
            mdgrid.postUpdateSubDomains();

            typedef MDGrid::SubDomainGrid SDGrid;
            const SDGrid& sdgrid = mdgrid.subDomain(0);
            SDGrid::LeafGridView sdgv = sdgrid.leafGridView();

            // solve problem
            run(sdgv,param);
        }
    }
    catch (Dune::Exception &e){
        std::cerr << "Dune reported error: " << e << std::endl;
        return 1;
    }
    //catch (...){
    //  std::cerr << "Unknown exception thrown!" << std::endl;
    //  return 1;
    //}
}

<file_sep>/WFEditSpatial/Converter/MorpheusConverter.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Converter
{
  public class MorpheusConverter : IDisposable
  {
    private readonly Dictionary<string, string> boundaryConditions;
    private readonly Dictionary<string, string> boundaryLables;
    private readonly Dictionary<string, Dictionary<string, string>> boundaryValue;
    private readonly Dictionary<string, string> coordinates;
    private readonly Dictionary<string, string> diffusion;
    private readonly Dimensions dims;
    private readonly SBMLDocument document;
    private readonly StringBuilder errorBuilder;
    private readonly Dictionary<string, string> initial;
    private readonly libsbmlcs.Model Model;
    private readonly int numVariables;

    public MorpheusConverter(SBMLDocument original)
    {
      errorBuilder = new StringBuilder();
      document = original.clone();
      numVariables = 0;

      document.getErrorLog().setSeverityOverride(libsbml.LIBSBML_OVERRIDE_DONT_LOG);
      var prop = new ConversionProperties();
      prop.addOption("replaceReactions", true,
        "Replace reactions with rateRules");
      var status = document.convert(prop);
      if (status != libsbml.LIBSBML_OPERATION_SUCCESS)
      {
        errorBuilder.AppendFormat("conversion of rates failed: {0}{1}", status, Environment.NewLine);
      }

      prop = new ConversionProperties();
      prop.addOption("expandFunctionDefinitions", true);
      status = document.convert(prop);
      if (status != libsbml.LIBSBML_OPERATION_SUCCESS)
      {
        errorBuilder.AppendFormat("expanding function definitions failed: {0}{1}", status, Environment.NewLine);
      }

      Model = document.getModel();


      boundaryLables = new Dictionary<string, string>();
      coordinates = new Dictionary<string, string>();
      for (var i = 0; i < Geometry.getNumCoordinateComponents(); ++i)
      {
        var current = Geometry.getCoordinateComponent(i);
        var prefix = "x";
        if (current.getType() == libsbml.SPATIAL_COORDINATEKIND_CARTESIAN_X)
          prefix = "x";
        else if (current.getType() == libsbml.SPATIAL_COORDINATEKIND_CARTESIAN_Y)
          prefix = "y";
        else if (current.getType() == libsbml.SPATIAL_COORDINATEKIND_CARTESIAN_Z)
          prefix = "z";

        coordinates[current.getId()] = prefix;

        boundaryLables[current.getBoundaryMin().getId()] = "-" + prefix;
        boundaryLables[current.getBoundaryMax().getId()] = prefix;
      }

      dims = new Dimensions();
      dims.setWidth(GetBoundaryMaxValue(0));
      dims.setHeight(GetBoundaryMaxValue(1));
      dims.setDepth(GetBoundaryMaxValue(2));

      boundaryConditions = new Dictionary<string, string>();
      diffusion = new Dictionary<string, string>();
      for (var i = 0; i < Model.getNumParameters(); ++i)
      {
        var current = Model.getParameter(i);
        var plugin = (SpatialParameterPlugin) current.getPlugin("spatial");
        if (plugin == null) continue;
        if (plugin.getType() == libsbml.SBML_SPATIAL_BOUNDARYCONDITION)
        {
          var condition = plugin.getBoundaryCondition();
          if (condition != null)
          boundaryConditions[condition.getCoordinateBoundary()] = TranslateCondition(condition.getType());
        }
        if (plugin.getType() == libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT)
        {
          var diff = plugin.getDiffusionCoefficient();
          if (diff != null)
          diffusion[diff.getVariable()] = current.getValue().ToString(CultureInfo.InvariantCulture);
        }
      }


      boundaryValue = new Dictionary<string, Dictionary<string, string>>();
      initial = new Dictionary<string, string>();
      for (var i = 0; i < Model.getNumSpecies(); ++i)
      {
        var current = Model.getSpecies(i);
        var plugin = (SpatialSpeciesPlugin) current.getPlugin("spatial");
        if (plugin == null || !plugin.getIsSpatial()) continue;

        ++numVariables;

        boundaryValue[current.getId()] = new Dictionary<string, string>();
        var bcValue = current.getXMinBC();
        if (bcValue.HasValue && Math.Abs(bcValue.Value) > 1e-16)
          boundaryValue[current.getId()]["-x"] = bcValue.Value.ToString(CultureInfo.InvariantCulture);
        bcValue = current.getXMaxBC();
        if (bcValue.HasValue && Math.Abs(bcValue.Value) > 1e-16)
          boundaryValue[current.getId()]["x"] = bcValue.Value.ToString(CultureInfo.InvariantCulture);
        bcValue = current.getYMinBC();
        if (bcValue.HasValue && Math.Abs(bcValue.Value) > 1e-16)
          boundaryValue[current.getId()]["-y"] = bcValue.Value.ToString(CultureInfo.InvariantCulture);
        bcValue = current.getYMaxBC();
        if (bcValue.HasValue && Math.Abs(bcValue.Value) > 1e-16)
          boundaryValue[current.getId()]["y"] = bcValue.Value.ToString(CultureInfo.InvariantCulture);

        if (current.isSetInitialConcentration())
          initial[current.getId()] = current.getInitialConcentration().ToString(CultureInfo.InvariantCulture);
        else if (current.isSetInitialAmount())
          initial[current.getId()] = current.getInitialAmount().ToString(CultureInfo.InvariantCulture);

        var assignment = Model.getInitialAssignment(current.getId());
        if (assignment == null) continue;
        initial[current.getId()] = TranslateExpression(assignment.getMath(), coordinates);
      }
    }

    public Geometry Geometry
    {
      get
      {
        var plugin = (SpatialModelPlugin) Model.getPlugin("spatial");
        if (plugin == null) return null;
        return plugin.getGeometry();
      }
    }

    public void Dispose()
    {
      if (document != null)
        document.Dispose();
      if (Model != null)
        Model.Dispose();
      if (dims != null)
        dims.Dispose();
    }

    public static string TranslateExpression(string expression)
    {
      return TranslateExpression(libsbml.parseL3Formula(expression), null);
    }

    public static string TranslateExpression(ASTNode math, Dictionary<string, string> map)
    {
      if (math == null) return "";
      switch (math.getType())
      {
        case libsbml.AST_INTEGER:
          return math.getInteger().ToString();
        case libsbml.AST_REAL:
        case libsbml.AST_REAL_E:
          return math.getReal().ToString(CultureInfo.InvariantCulture);
        case libsbml.AST_DIVIDE:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          builder.AppendMorpheusNode(" / {0}", math.getChild(1), map);
          return builder.ToString();
        }
        case libsbml.AST_MINUS:
        {
          var builder = new StringBuilder();
          if (math.getNumChildren() == 1)
          {
            builder.AppendMorpheusNode(" - {0}", math.getChild(0), map);
          }
          else
          {
            builder.AppendMorpheusNode("{0}", math.getChild(0), map);
            builder.AppendMorpheusNode(" - {0}", math.getChild(1), map);
          }
          return builder.ToString();
        }
        case libsbml.AST_PLUS:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendMorpheusNode(" + {0}", math.getChild(i), map);
          }
          return builder.ToString();
        }
        case libsbml.AST_TIMES:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendMorpheusNode(" * {0}", math.getChild(i), map);
          }
          return builder.ToString();
        }

        case libsbml.AST_RELATIONAL_LEQ:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          builder.AppendMorpheusNode(" <= {0}", math.getChild(1), map);
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_LT:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          builder.AppendMorpheusNode(" < {0}", math.getChild(1), map);
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_GT:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          builder.AppendMorpheusNode(" > {0}", math.getChild(1), map);
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_GEQ:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          builder.AppendMorpheusNode(" >= {0}", math.getChild(1), map);
          return builder.ToString();
        }
        case libsbml.AST_LOGICAL_AND:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendMorpheusNode(" and {0}", math.getChild(i), map);
          }
          return builder.ToString();
        }
        case libsbml.AST_LOGICAL_OR:
        {
          var builder = new StringBuilder();
          builder.AppendMorpheusNode("{0}", math.getChild(0), map);
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendMorpheusNode(" or {0}", math.getChild(i), map);
          }
          return builder.ToString();
        }
        case libsbml.AST_FUNCTION_PIECEWISE:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("if(");
          for (var i = 0; i < math.getNumChildren() - 1; i += 2)
          {
            builder.AppendMorpheusNode("{0}", math.getChild(i + 1), map);
            builder.AppendMorpheusNode(", {0}", math.getChild(i), map);
          }
          builder.AppendMorpheusNode(", {0}", math.getChild(math.getNumChildren() - 1), map);
          builder.AppendFormat(")");
          return builder.ToString();
        }
        case libsbml.AST_FUNCTION:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("{0}(", math.getName());
          builder.AppendFormat("{0}", TranslateExpression(math.getChild(0), map));
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendFormat(", {0}", TranslateExpression(math.getChild(i), map));
          }
          builder.AppendFormat(")");
          return builder.ToString();
        }
        case libsbml.AST_FUNCTION_POWER:
        case libsbml.AST_POWER:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("pow({0}", TranslateExpression(math.getChild(0), map));
          builder.AppendFormat(", {0})", TranslateExpression(math.getChild(1), map));
          return builder.ToString();
        }
        //case libsbml.AST_NAME:
        default:
          if (map != null && map.ContainsKey(math.getName()))
          {
            return "l." + map[math.getName()];
          }
          return math.getName();
      }
    }

    private string TranslateCondition(string type)
    {
      switch (type.ToLowerInvariant())
      {
        default:
        //case "flux":
          return "constant";
        case "value":
          return "noflux";
      }
    }

    private string TranslateCondition(int type)
    {
      switch (type)
      {
        default:
        case libsbml.SPATIAL_BOUNDARYKIND_NEUMANN:
          //case "flux":
          return "constant";
        case libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET:
          return "noflux";
      }
    }

    public string ToMorpheus(string filename = null)
    {
      var settings = new XmlWriterSettings {Indent = true, Encoding = Encoding.UTF8, OmitXmlDeclaration = true};
      var buffer = new StringBuilder();
      var writer = XmlWriter.Create(buffer, settings);

      writer.WriteStartDocument();

      writer.WriteStartElement("MorpheusModel");
      writer.WriteAttributeString("version", "1");

      WriteDescription(writer);
      WriteSpace(writer, filename);
      WriteTime(writer);
      WritePDE(writer);
      WriteAnalysis(writer);

      writer.WriteEndElement(); // MorpheusModel

      writer.WriteEndDocument();
      writer.Flush();
      writer.Close();
      return buffer.ToString();
    }

    private void WriteAnalysis(XmlWriter writer)
    {
      var multiplier = Math.Max(1, numVariables/2);
      const double scale = 4;

      writer.WriteStartElement("Analysis");
      writer.WriteStartElement("Gnuplotter");
      writer.WriteAttributeString("interval", "1");
      writer.WriteAttributeString("timename", "false");
      writer.WriteStartElement("Terminal");
      writer.WriteAttributeString("size",
        string.Format("{0} {1} {2}",
          Math.Max(200*multiplier, scale*dims.getWidth()*multiplier),
          Math.Max(200*multiplier, scale*dims.getHeight()*multiplier),
          scale*dims.getDepth())
        );
      writer.WriteAttributeString("name", "png");
      writer.WriteEndElement(); // Terminal


      for (var i = 0; i < Model.getNumSpecies(); i++)
      {
        var current = Model.getSpecies(i);
        var plugin = (SpatialSpeciesPlugin) current.getPlugin("spatial");
        if (plugin == null) continue;
        if (plugin.getIsSpatial())
        {
          writer.WriteStartElement("PDE");
          writer.WriteAttributeString("symbol-ref", current.getId());
          writer.WriteAttributeString("min", "0");
          writer.WriteEndElement(); // PDE
        }
      }

      writer.WriteEndElement(); // Gnuplotter
      writer.WriteEndElement(); // Analysis
    }

    private double GetBoundaryMaxValue(int val, double defaultValue = 0)
    {
      if (val >= Geometry.getNumCoordinateComponents()) return defaultValue;
      try
      {
        return Geometry.getCoordinateComponent(val).getBoundaryMax().getValue();
      }
      catch
      {
        return defaultValue;
      }
    }

    private void WritePDE(XmlWriter writer)
    {
      writer.WriteStartElement("PDE");

      for (var i = 0; i < Model.getNumSpecies(); i++)
      {
        var current = Model.getSpecies(i);
        var plugin = (SpatialSpeciesPlugin) current.getPlugin("spatial");
        if (plugin == null) continue;
        if (!plugin.getIsSpatial()) continue;

        writer.WriteStartElement("Layer");
        writer.WriteAttributeString("symbol", current.getId());
        writer.WriteAttributeString("name", current.isSetName() ? current.getName() : current.getId());

        writer.WriteStartElement("Diffusion");
        writer.WriteAttributeString("rate",
          diffusion.ContainsKey(current.getId()) ? diffusion[current.getId()] : "0");
        writer.WriteEndElement(); // Diffusion

        writer.WriteStartElement("Initial");
        writer.WriteStartElement("InitPDEExpression");
        writer.WriteStartElement("Expression");
        writer.WriteString(initial[current.getId()]);
        writer.WriteEndElement(); // Expression 
        writer.WriteEndElement(); // InitPDEExpression
        writer.WriteEndElement(); // Initial

        if (boundaryValue.ContainsKey(current.getId()))
        {
          var bounds = boundaryValue[current.getId()];
          foreach (var item in bounds)
          {
            writer.WriteStartElement("BoundaryValue");
            writer.WriteAttributeString("boundary", item.Key);
            writer.WriteAttributeString("value", item.Value);
            writer.WriteEndElement(); // BoundaryValue
          }
        }

        writer.WriteEndElement(); // Layer
      }


      WriteSystem(writer);


      writer.WriteEndElement(); // PDE
    }

    private string SimplifyExpression(string expression)
    {
      var result = expression.Replace("+ ( - 1) *", "-");
      result = result.Replace(" +  - ", " - ");
      result = result.Replace(" 1 * ", " ");
      result = result.Replace(" * 1 ", " ");
      if (result.StartsWith("1 * "))
        result = result.Substring("1 * ".Length);
      return result;
    }

    private void WriteSystem(XmlWriter writer)
    {
      writer.WriteStartElement("System");
      writer.WriteAttributeString("solver", "runge-kutta");
      writer.WriteAttributeString("time-step", "0.01");
      writer.WriteAttributeString("name", Model.isSetName() ? Model.getName() : Model.getId());

      for (var i = 0; i < Model.getNumCompartments(); ++i)
      {
        var current = Model.getCompartment(i);

        writer.WriteStartElement("Constant");
        writer.WriteAttributeString("symbol", current.getId());
        writer.WriteAttributeString("value", current.getSize().ToString(CultureInfo.InvariantCulture));
        writer.WriteEndElement(); // Constant
      }
      for (var i = 0; i < Model.getNumParameters(); ++i)
      {
        var current = Model.getParameter(i);
        var plugin = (SpatialParameterPlugin) current.getPlugin("spatial");
        if (plugin != null && plugin.getType() != -1) continue;

        writer.WriteStartElement("Constant");
        writer.WriteAttributeString("symbol", current.getId());
        writer.WriteAttributeString("value", current.getValue().ToString(CultureInfo.InvariantCulture));
        writer.WriteEndElement(); // Constant
      }

      for (var i = 0; i < Model.getNumRules(); ++i)
      {
        var current = Model.getRule(i);
        if (current.isRate())
        {
          writer.WriteStartElement("DiffEqn");
          writer.WriteAttributeString("symbol-ref", current.getVariable());
          writer.WriteStartElement("Expression");
          writer.WriteString(SimplifyExpression(TranslateExpression(current.getMath(), coordinates)));
          writer.WriteEndElement(); // Expression
          writer.WriteEndElement(); // DiffEqn
        }
      }

      writer.WriteEndElement(); // System
    }

    private void WriteTime(XmlWriter writer)
    {
      writer.WriteStartElement("Time");
      writer.WriteStartElement("StartTime");
      writer.WriteAttributeString("unit", "sec");
      writer.WriteAttributeString("value", "0");
      writer.WriteEndElement(); // StartTime
      writer.WriteStartElement("StopTime");
      writer.WriteAttributeString("unit", "sec");
      writer.WriteAttributeString("value", (60*60*24).ToString());
      writer.WriteEndElement(); // StopTime
      writer.WriteStartElement("SaveInterval");
      writer.WriteAttributeString("value", "0");
      writer.WriteEndElement(); // SaveInterval
      writer.WriteStartElement("RandomSeed");
      writer.WriteAttributeString("value", "2");
      writer.WriteEndElement(); // RandomSeed
      writer.WriteEndElement(); // Time
    }

    private void WriteSpace(XmlWriter writer, string filename = null)
    {
      writer.WriteStartElement("Space");
      writer.WriteStartElement("Lattice");
      writer.WriteAttributeString("class", "square");
      writer.WriteStartElement("Size");
      writer.WriteAttributeString("symbol", "size");
      writer.WriteAttributeString("value",
        string.Format("{0} {1} {2}",
          dims.getWidth(),
          dims.getHeight(),
          dims.getDepth())
        );
      writer.WriteEndElement(); // Size
      writer.WriteStartElement("NodeLength");
      writer.WriteAttributeString("value", "1");
      writer.WriteEndElement(); // NodeLength
      writer.WriteStartElement("BoundaryConditions");

      foreach (var entry in boundaryConditions)
      {
        writer.WriteStartElement("Condition");
        writer.WriteAttributeString("boundary", boundaryLables[entry.Key]);
        writer.WriteAttributeString("type", entry.Value);
        writer.WriteEndElement(); // Condition        
      }
      writer.WriteEndElement(); // BoundaryConditions

      // write domain (if analytic)
      var analyticGeometry = Geometry.GetFirstAnalyticGeometry();
      if (filename != null && analyticGeometry != null && analyticGeometry.getNumAnalyticVolumes() > 0)
      {
        var path = Path.GetDirectoryName(filename);
        var name = Path.GetFileNameWithoutExtension(filename);
        if (!string.IsNullOrWhiteSpace(path) && !string.IsNullOrWhiteSpace(name))
        {
          var vol = analyticGeometry.getAnalyticVolume(0);
          writer.WriteStartElement("Domain");
          writer.WriteAttributeString("boundary-type", boundaryConditions.Values.First());
          writer.WriteStartElement("Image");
          var image = analyticGeometry.GenerateTiffForOrdinal(Geometry, (int) vol.getOrdinal());
          image.Save(Path.Combine(path, name + ".tif"), ImageFormat.Tiff);
          writer.WriteAttributeString("path", name + ".tif");
          writer.WriteEndElement(); // Image
          writer.WriteEndElement(); // Domain
        }
      }

      writer.WriteEndElement(); // Lattice
      writer.WriteStartElement("SpaceSymbol");
      writer.WriteAttributeString("symbol", "l");
      writer.WriteAttributeString("name", "location");
      writer.WriteEndElement(); // SpaceSymbol
      writer.WriteEndElement(); // Space
    }

    private void WriteDescription(XmlWriter writer)
    {
      writer.WriteStartElement("Description");
      writer.WriteStartElement("Title");
      if (Model.isSetName())
        writer.WriteString(Model.getName());
      else
        writer.WriteString(Model.getId());
      writer.WriteEndElement(); // Title
      writer.WriteStartElement("Details");
      var errorString = errorBuilder.ToString();
      if (!string.IsNullOrWhiteSpace(errorString))
        writer.WriteString("Conversion messages: " + errorString);
      writer.WriteEndElement(); // Details
      writer.WriteEndElement(); // Description
    }

    public string ToSBML()
    {
      return libsbml.writeSBMLToString(document);
    }
  }
}<file_sep>/LibEditSpatial/Model/DomainConfig.cs
using System.Collections.Generic;

namespace LibEditSpatial.Model
{
  public class DomainConfig
  {
    public string Geometry { get; set; }
    public double GeometryMin { get; set; }
    public double GeometryMax { get; set; }
    public int Dimensions { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
    public double Depth { get; set; }
    public int GridX { get; set; }
    public int GridY { get; set; }
    public int GridZ { get; set; }
    public int Refinement { get; set; }

    public static DomainConfig Default
    {
      get
      {
        return new DomainConfig
        {
          Width = 100,
          Height = 100,
          Depth = 1,
          GridX = 64,
          GridY = 64,
          GridZ = 1,
          Refinement = 0
        };
      }
    }

    public static DomainConfig FromDict(Dictionary<string, string> dict)
    {
      var result = new DomainConfig
      {
        Geometry = dict.Get<string>("geometry"),
        GeometryMin = dict.Get<double>("geometry_min"),
        GeometryMax = dict.Get<double>("geometry_max"),
        Dimensions = dict.Get<int>("dim"),
        Width = dict.Get<double>("width"),
        Height = dict.Get<double>("height"),
        Depth = dict.Get<double>("depth"),
        GridX = dict.Get<int>("nx"),
        GridY = dict.Get<int>("ny"),
        GridZ = dict.Get<int>("nz"),
        Refinement = dict.Get<int>("refine")
      };
      return result;
    }

    public Dictionary<string, string> ToDict()
    {
      var result = new Dictionary<string, string>();
      result["geometry"] = Geometry;
      result["geometry_min"] = GeometryMin.ToString();
      result["geometry_max"] = GeometryMax.ToString();
      result["dim"] = Dimensions.ToString();
      result["width"] = Width.ToString();
      result["height"] = Height.ToString();
      result["depth"] = Depth.ToString();
      result["nx"] = GridX.ToString();
      result["ny"] = GridY.ToString();
      result["nz"] = GridZ.ToString();
      result["refine"] = Refinement.ToString();
      return result;
    }
  }
}<file_sep>/WFEditSpatial/Converter/DuneConverter.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using EditSpatial.Model;
using EditSpatial.Properties;
using libsbmlcs;

namespace EditSpatial.Converter
{
  public class DuneConverter : IDisposable
  {
    private readonly SBMLDocument _Document;
    private readonly StringBuilder _errorBuilder;
    private readonly Geometry _Geometry;
    private readonly libsbmlcs.Model _Model;
    private readonly SBMLDocument _Original;
    private readonly SpatialModelPlugin SpatialModelPlugin;

    private List<string> AdditionalVars;

    /// <summary>
    /// This function tags all reactions in the document with a factor that indicates whether 
    /// it is spanning multiple compartments.
    /// </summary>
    /// <param name="document"></param>
    private void TagReactions(SBMLDocument document)
    {
      AdditionalVars = new List<string>();

      var model = document.getModel();
      if (model == null) return;
      if (model.getNumCompartments() < 2) return;

      var numReactions = model.getNumReactions();
      for (int i = 0; i < numReactions; ++i)
      {
        var compartments = new List<string>();
        var reaction = model.getReaction(i);

        if (!reaction.isSetKineticLaw()) continue;

        var kinetics = reaction.getKineticLaw();
        if (kinetics == null || !kinetics.isSetMath()) continue;

        var numSubstrates = reaction.getNumReactants();
        var numProducts = reaction.getNumProducts();
        var numModifiers = reaction.getNumModifiers();
        if (numSubstrates + numProducts < 2) continue;

        for (int j = 0; j < numSubstrates; ++j)
        {
          var current = reaction.getReactant(j);
          if (!current.isSetSpecies()) continue;
          var species = model.getSpecies(current.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          string compartment = species.getCompartment();
          if (!compartments.Contains(compartment))
          {
            compartments.Add(compartment);
          }
        }

        for (int j = 0; j < numProducts; ++j)
        {
          var current = reaction.getProduct(j);
          if (!current.isSetSpecies()) continue;
          var species = model.getSpecies(current.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          string compartment = species.getCompartment();
          if (!compartments.Contains(compartment))
          {
            compartments.Add(compartment);
          }
        }

        for (int j = 0; j < numModifiers; ++j)
        {
          var current = reaction.getModifier(j);
          if (!current.isSetSpecies()) continue;
          var species = model.getSpecies(current.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          string compartment = species.getCompartment();
          if (!compartments.Contains(compartment))
          {
            compartments.Add(compartment);
          }
        }

        if (compartments.Count < 2) continue;

        compartments.Sort();

        var formula = new StringBuilder();
        foreach (var id in compartments)
        {
          formula.AppendFormat("isIn_{0} * ", id);
          if (!AdditionalVars.Contains("isIn_" + id))
          {
            // create parameter temporarily to get passt validation later on
            var param = model.createParameter();
            param.setId("isIn_" + id);            
            AdditionalVars.Add("isIn_" + id);
          }
        }

        formula.AppendFormat("({0})", libsbml.formulaToL3String(kinetics.getMath()));
        kinetics.setMath(libsbml.parseL3Formula(formula.ToString()));

      }

    }

    public DuneConverter(SBMLDocument original)
    {
      OdeVariables = new List<string>();
      _errorBuilder = new StringBuilder();
      _Document = original.clone();
      _Original = original;

      TagReactions(_Document);

      _Document.getErrorLog().setSeverityOverride(libsbml.LIBSBML_OVERRIDE_DONT_LOG);
      var prop = new ConversionProperties();
      prop.addOption("promoteLocalParameters", true,
        "Promotes all Local Parameters to Global ones");
      var status = _Document.convert(prop);
      if (status != libsbml.LIBSBML_OPERATION_SUCCESS)
      {
        _errorBuilder.AppendFormat("promoting local to global parameters failed: {0}{1}", status, Environment.NewLine);
      }

      prop = new ConversionProperties();
      prop.addOption("expandFunctionDefinitions", true);
      status = _Document.convert(prop);
      if (status != libsbml.LIBSBML_OPERATION_SUCCESS)
      {
        _errorBuilder.AppendFormat("expanding function definitions failed: {0}{1}", status, Environment.NewLine);
      }

      prop = new ConversionProperties();
      prop.addOption("replaceReactions", true,
        "Replace reactions with rateRules");
      status = _Document.convert(prop);
      if (status != libsbml.LIBSBML_OPERATION_SUCCESS)
      {
        _errorBuilder.AppendFormat("conversion of rates failed: {0}{1}", status, Environment.NewLine);
      }

      prop = new ConversionProperties();
      prop.addOption("renameSIds", true);
      prop.addOption("currentIds", "default");
      prop.addOption("newIds", "__default");
      status = _Document.convert(prop);
      if (status != libsbml.LIBSBML_OPERATION_SUCCESS)
      {
        _errorBuilder.AppendFormat("rename of ids failed: {0}{1}", status, Environment.NewLine);
      }

      _Model = _Document.getModel();

      // remove parameters added only to pass validation
      foreach (var id in AdditionalVars)
      {
        _Model.removeParameter(id);
      }

      for (var i = 0; i < _Model.getNumRules(); ++i)
      {
        var current = _Model.getRule(i);
        if (current == null || current.getTypeCode() != libsbml.SBML_RATE_RULE) continue;
        OdeVariables.Add(current.getVariable());
      }

      SpatialModelPlugin = (SpatialModelPlugin) _Model.getPlugin("spatial");

      if (SpatialModelPlugin == null) return;

      _Geometry = SpatialModelPlugin.getGeometry();
    }

    public string Errors
    {
      get
      {
        if (_errorBuilder == null) return "";
        return _errorBuilder.ToString();
      }
    }

    public double GeometryMin { get; set; }
    public double GeometryMax { get; set; }
    private List<string> OdeVariables { get; set; }
    private List<string> ParameterIds { get; set; }
    public string GeometryFile { get; set; }

    public void Dispose()
    {
      if (_Document != null)
        _Document.Dispose();
      if (_Model != null)
        _Model.Dispose();
      if (SpatialModelPlugin != null)
        SpatialModelPlugin.Dispose();
      if (_Geometry != null)
        _Geometry.Dispose();
    }

    public static string TranslateExpression(string expression, Dictionary<string, string> map = null,
      libsbmlcs.Model model = null)
    {
      return TranslateExpression(libsbml.parseL3Formula(expression), map, model);
    }

    public static string TranslateExpression(ASTNode math, Dictionary<string, string> map = null,
      libsbmlcs.Model model = null)
    {
      if (math == null) return "";
      switch (math.getType())
      {
        case libsbml.AST_INTEGER:
          return math.getInteger().ToString("E17");
        case libsbml.AST_REAL:
        case libsbml.AST_REAL_E:
          return math.getReal().ToString("E17");
        case libsbml.AST_FUNCTION_POWER:
        {
          {
            var builder = new StringBuilder();
            builder.AppendDuneNode("pow({0}", math.getChild(0), map, model);
            builder.AppendDuneNode(", {0})", math.getChild(1), map, model);
            return builder.ToString();
          }
        }
        case libsbml.AST_DIVIDE:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          builder.AppendDuneNode(" / {0}", math.getChild(1), map, model);
          return builder.ToString();
        }
        case libsbml.AST_MINUS:
        {
          var builder = new StringBuilder();
          if (math.getNumChildren() == 1)
          {
            builder.AppendDuneNode(" - {0}", math.getChild(0), map, model);
          }
          else
          {
            builder.AppendDuneNode("{0}", math.getChild(0), map, model);
            builder.AppendDuneNode(" - {0}", math.getChild(1), map, model);
          }
          return builder.ToString();
        }
        case libsbml.AST_PLUS:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendDuneNode(" + {0}", math.getChild(i), map, model);
          }
          return builder.ToString();
        }
        case libsbml.AST_TIMES:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendDuneNode(" * {0}", math.getChild(i), map, model);
          }
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_LEQ:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          builder.AppendDuneNode(" <= {0}", math.getChild(1), map, model);
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_LT:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          builder.AppendDuneNode(" < {0}", math.getChild(1), map, model);
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_GT:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          builder.AppendDuneNode(" > {0}", math.getChild(1), map, model);
          return builder.ToString();
        }
        case libsbml.AST_RELATIONAL_GEQ:
        {
          var builder = new StringBuilder();
          builder.AppendDuneNode("{0}", math.getChild(0), map, model);
          builder.AppendDuneNode(" >= {0}", math.getChild(1), map, model);
          return builder.ToString();
        }
        case libsbml.AST_POWER:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("pow({0}", TranslateExpression(math.getChild(0), map, model));
          builder.AppendFormat(", {0})", TranslateExpression(math.getChild(1), map, model));
          return builder.ToString();
        }
        case libsbml.AST_LOGICAL_AND:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("({0}", TranslateExpression(math.getChild(0), map, model));
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendFormat(" && {0}", TranslateExpression(math.getChild(i), map, model));
          }
          builder.AppendFormat(")");
          return builder.ToString();
        }
        case libsbml.AST_LOGICAL_NOT:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("!({0})", TranslateExpression(math.getChild(0), map, model));
          return builder.ToString();
        }
        case libsbml.AST_LOGICAL_OR:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("({0}", TranslateExpression(math.getChild(0), map, model));
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendFormat(" || {0}", TranslateExpression(math.getChild(i), map, model));
          }
          builder.AppendFormat(")");
          return builder.ToString();
        }
        case libsbml.AST_FUNCTION_PIECEWISE:
        {
          var builder = new StringBuilder();
          builder.AppendFormat("(");
          for (var i = 0; i < math.getNumChildren() - 1; i += 2)
          {
            builder.AppendFormat("({0}) ", TranslateExpression(math.getChild(i + 1), map, model));
            builder.AppendFormat("? {0} :", TranslateExpression(math.getChild(i), map, model));
          }
          builder.AppendFormat(" {0}", TranslateExpression(math.getChild(math.getNumChildren() - 1), map, model));
          builder.AppendFormat(")");
          return builder.ToString();
        }
        case libsbml.AST_FUNCTION:
        {
          var name = math.getName();
          var builder = new StringBuilder();
          builder.AppendFormat("{0}(", name);
          builder.AppendFormat("{0}", TranslateExpression(math.getChild(0), map, model));
          for (var i = 1; i < math.getNumChildren(); ++i)
          {
            builder.AppendFormat(", {0}", TranslateExpression(math.getChild(i), map, model));
          }
          builder.AppendFormat(")");
          return builder.ToString();
        }
        //case libsbml.AST_NAME:
        default:
        {
          var name = math.getName();
          if (map != null && map.ContainsKey(name))
          {
            if (map.ContainsKey("use_compartments") && model != null)
            {
              var species = model.getSpecies(name);
              if (species == null || !species.isSetCompartment()) return map[name];
              return String.Format("isIn_{0} * {1}", species.getCompartment(), map[name]);
            }
            return map[name];
          }
          return name;
        }
      }
    }

    public string ToSBML()
    {
      return libsbml.writeSBMLToString(_Document);
    }

    public void ExportTo(string filename)
    {
      var path = Path.GetDirectoryName(filename);
      var name = Path.GetFileNameWithoutExtension(filename);
      WriteCMakeLists(path, name);
      WriteConfigFile(path, name);
      WriteComponentParameters(path);
      WriteInitialConditions(path);
      WriteLocalOperator(path);
      WriteMainFile(path, name);
      WriteReactionAdapter(path);
      if (_Original != null && path != null)
        libsbml.writeSBMLToFile(_Original, Path.Combine(path, "sbml.xml"));
    }

    private void WriteReactionAdapter(string path)
    {
      var map = new Dictionary<string, string>();

      var initializer = new StringBuilder();
      var declarations = new StringBuilder();
      var odes = new StringBuilder();
      var odes2 = new StringBuilder();

      foreach (var item in ParameterIds)
      {
        initializer.AppendFormat(
          "	   , {0}(param.sub(\"Reaction\").template get<RF>(\"{0}\")){1}"
          , item
          , Environment.NewLine
          );
        declarations.AppendFormat(
          "    RF {0};{1}"
          , item
          , Environment.NewLine
          );
      }

      var count = 0;
      odes.AppendLine("        //// variable map: variable -> x(lsfu,...)");
      var varMap = new Dictionary<string, string>();
      foreach (var item in OdeVariables)
      {
        varMap[item] = string.Format("x(lfsu,{0})", count);
        odes.AppendFormat("        // {0} -> {1}{2}", item, varMap[item], Environment.NewLine);
        ++count;
      }
      odes.AppendLine();
      odes.AppendLine();


      var numCompartments = _Model.getNumCompartments();
      if (numCompartments > 1)
      {
        varMap["use_compartments"] = "1";
        odes.AppendLine("        //// determine whether current location is in a compartment");
        for (var i = 0; i < numCompartments; ++i)
        {
          var comp = _Model.getCompartment(i);
          var compId = comp.getId();
          initializer.AppendFormat(
            "	   , dh_in{0}(DataHelper::forFile(param.sub(\"Reaction\").template get<std::string>(\"in_{0}\", \"\"), NearestNeighbor)){1}"
            , compId
            , Environment.NewLine
            );
          declarations.AppendFormat(
            "    const DataHelper* dh_in{0};{1}"
            , compId
            , Environment.NewLine
            );
          odes.AppendFormat(
            "        RF isIn_{0} = dh_in{0} == NULL ? 1 :(RF)dh_in{0}->get((double)pos[0], (double)pos[1]);{1}", compId,
            Environment.NewLine);
        }
      }


      odes.AppendLine();
      odes.AppendLine();
      odes.AppendLine("        //// calculate assignment rules");
      for (var i = 0; i < _Model.getNumRules(); ++i)
      {
        var rule = _Model.getRule(i) as AssignmentRule;
        if (rule == null || rule.getTypeCode() != libsbml.SBML_ASSIGNMENT_RULE) continue;
        odes.AppendFormat("        {0} = {1};{2}",
          TranslateExpression(rule.getVariable(), varMap),
          TranslateExpression(rule.getMath(), varMap, _Model),
          Environment.NewLine
          );
      }
      odes.AppendLine();
      odes.AppendLine();

      count = 1;
      odes.AppendLine("        //// calculate rate rules");
      foreach (var item in OdeVariables)
      {
        var rule = _Model.getRateRule(item);
        if (rule == null || !rule.isSetMath()) continue;

        odes.AppendFormat("        // {0} {1}", item, Environment.NewLine);
        var species = _Model.getSpecies(rule.getVariable());
        if (species != null && numCompartments > 1 && species.isSetCompartment())
          odes.AppendFormat("        RF r{0} = isIn_{1}*({2});{3}",
            count,
            species.getCompartment(),
            TranslateExpression(rule.getMath(), varMap, _Model),
            Environment.NewLine
            );
        else
          odes.AppendFormat("        RF r{0} = {1};{2}",
            count,
            TranslateExpression(rule.getMath(), varMap, _Model),
            Environment.NewLine
            );

        odes2.AppendFormat("        r.accumulate(lfsv,{0},-r{1}*eg.geometry().volume());{2}",
          count - 1,
          count,
          Environment.NewLine
          );
        odes.AppendLine();
        ++count;
      }

      odes.AppendLine();
      odes.AppendLine(odes2.ToString());


      map["%INITIALIZER%"] = initializer.ToString();
      map["%DECLARATION%"] = declarations.ToString();
      map["%ODES%"] = odes.ToString();

      File.WriteAllText(Path.Combine(path, "reactionadapter.hh"),
        ExpandTemplate(Resources.reactionadapter, map));
    }

    private int GetBoundaryType(Parameter param)
    {
      var plug = (SpatialParameterPlugin) param.getPlugin("spatial");
      if (plug == null) return -1;
      var bc = plug.getBoundaryCondition();
      if (bc == null) return -1;
      if (bc.getType() == libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET) return 1; // Dirichlet
      return -1; // Neumann
    }

    private int GetBoundaryType(Parameter xmin, Parameter xmax, Parameter ymin, Parameter ymax)
    {
      if (xmin != null) return GetBoundaryType(xmin);
      if (xmax != null) return GetBoundaryType(xmax);
      if (ymin != null) return GetBoundaryType(ymin);
      if (ymax != null) return GetBoundaryType(ymax);
      return -1;
    }

    private void WriteConfigFile(string path, string name)
    {
      var builder = new StringBuilder();
      var map = new Dictionary<string, string>();

      map["%GEOMETRY_FILE%"] = GeometryFile;
      map["%GEOMETRY_MIN%"] = GeometryMin.ToString(CultureInfo.InvariantCulture);
      map["%GEOMETRY_MAX%"] = GeometryMax.ToString(CultureInfo.InvariantCulture);

      map["%NAME%"] = name;
      map["%TIME_TEND%"] = "500";
      map["%TIME_DTMAX%"] = "1";
      map["%TIME_DTPLOT%"] = "1";

      map["%WORLD_WIDTH%"] = _Geometry.getCoordinateComponent(0).getBoundaryMax().getValue().ToString(CultureInfo.InvariantCulture);
      map["%WORLD_HEIGHT%"] = _Geometry.getCoordinateComponent(1).getBoundaryMax().getValue().ToString(CultureInfo.InvariantCulture);

      map["%GRID_X%"] = "64";
      map["%GRID_Y%"] = "64";

      builder.AppendLine(ExpandTemplate(Resources.config, map));

      // add reaction section
      builder.AppendLine("[Reaction]");
      ParameterIds = new List<string>();
      for (var i = 0; i < _Model.getNumParameters(); ++i)
      {
        var current = _Model.getParameter(i);
        if (current.IsSpatial()) continue;

        ParameterIds.Add(current.getId());
        double currentValue = current.getValue();
        if (double.IsNaN(currentValue)) currentValue = 0;
        builder.AppendFormat("{0} = {1}{2}", current.getId(), currentValue, Environment.NewLine);
      }

      for (var i = 0; i < _Model.getNumCompartments(); ++i)
      {
        var current = _Model.getCompartment(i);

        ParameterIds.Add(current.getId());
        builder.AppendFormat("{0} = {1}{2}", current.getId(), current.getSize(), Environment.NewLine);
      }

      for (var i = 0; i < _Model.getNumSpecies(); ++i)
      {
        var current = _Model.getSpecies(i);
        if (current == null || !current.getBoundaryCondition()) continue;
        ParameterIds.Add(current.getId());
        double currentValue = current.getInitialConcentration();
        if (!current.isSetInitialConcentration()) currentValue = current.getInitialAmount();
        builder.AppendFormat("{0} = {1}{2}", current.getId(), currentValue, Environment.NewLine);
      }

      builder.AppendLine();

      // add component sections
      for (var i = 0; i < OdeVariables.Count; ++i)
      {
        var species = _Model.getSpecies(OdeVariables[i]);
        if (species == null) continue;
        var plug = (SpatialSpeciesPlugin)species.getPlugin("spatial");
        if (plug == null || plug.getIsSpatial() == false) continue;

        var diff = species.getDiffusionX();
        if (!diff.HasValue)
          diff = species.getDiffusionY();

        if (!diff.HasValue) diff = 0;

        var Xmin = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Xmin");
        var Xmax = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Xmax");
        if (Xmin == null) Xmin = Xmax;
        if (Xmax == null) Xmax = Xmin;
        var Ymin = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Ymin");
        var Ymax = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Ymax");
        if (Ymin == null) Ymin = Ymax;
        if (Ymax == null) Ymax = Ymin;

        var type = GetBoundaryType(Xmin, Xmax, Ymin, Ymax);

        builder.AppendFormat("[{0}]{1}", species.getId(), Environment.NewLine);
        builder.AppendFormat("# Species '{0}' in Compartment '{1}'{2}", species.getId(), species.getCompartment(),
          Environment.NewLine);
        builder.AppendFormat("D = {0}{1}", diff.Value, Environment.NewLine);
        builder.AppendFormat("Xmin = {0}{1}", Xmin == null ? 0 : Xmin.getValue(), Environment.NewLine);
        builder.AppendFormat("Xmax = {0}{1}", Xmax == null ? 0 : Xmax.getValue(), Environment.NewLine);
        builder.AppendFormat("Ymin = {0}{1}", Ymin == null ? 0 : Ymin.getValue(), Environment.NewLine);
        builder.AppendFormat("Ymax = {0}{1}", Ymax == null ? 0 : Ymax.getValue(), Environment.NewLine);
        builder.AppendFormat("# Dirichlet=1, Neumann=-1, Outflow=-2, None=-3{0}", Environment.NewLine);
        builder.AppendFormat("BCType = {0}{1}", type, Environment.NewLine);

        builder.AppendLine();
      }

      builder.AppendLine();
      builder.AppendLine("[Data]");
      builder.AppendLine("# use this section to override initial conditions of any variable");
      builder.AppendLine("# by simply specifying the variable followed by a filename");
      builder.AppendLine("# of the dmp file containing the initial values");
      foreach (var item in OdeVariables)
        builder.AppendFormat("{0} = {1}", item, Environment.NewLine);
      builder.AppendLine();

      File.WriteAllText(Path.Combine(path, name + ".conf"),
        builder.ToString());
    }

    private void WriteMainFile(string path, string name)
    {
      var builder = new StringBuilder();
      var map = new Dictionary<string, string>();

      var dpCreation = new StringBuilder();
      var dpInitialization = new StringBuilder();
      var subCreation = new StringBuilder();
      var initialTypeCreation = new StringBuilder();
      var initialTypes = new StringBuilder();
      var initialArgs = new StringBuilder();
      var gridFunctions = new StringBuilder();

      map["%NUMCOMPONENTS%"] = OdeVariables.Count.ToString();
      map["%GEOMETRY%"] = GenerateGeometryExpression(path);

      for (var index = 0; index < OdeVariables.Count; index++)
      {
        dpCreation.AppendFormat("    DP dp{0}(param, \"{1}\");{2}"
          , index + 1
          , OdeVariables[index]
          , Environment.NewLine);

        dpInitialization.AppendFormat("dp{0}", index + 1);
        initialTypes.AppendFormat("U{0}InitialType", index);
        initialArgs.AppendFormat("u{0}initial", index);
        if (index + 1 < OdeVariables.Count)
        {
          dpInitialization.Append(",");
          initialTypes.Append(",");
          initialArgs.Append(",");
        }

        subCreation.AppendFormat(
          "    typedef Dune::PDELab::GridFunctionSubSpace<TPGFS,Dune::TypeTree::TreePath<{0}> > U{0}SUB;{1}"
          , index, Environment.NewLine);
        subCreation.AppendFormat("    U{0}SUB u{0}sub(tpgfs);{1}"
          , index, Environment.NewLine);


        initialTypeCreation.AppendFormat("    UGeneralInitialType u{0}initial(gv,param,{0});{1}"
          , index, Environment.NewLine);

        gridFunctions.AppendFormat("    typedef Dune::PDELab::DiscreteGridFunction<U{0}SUB,V> U{0}DGF;{1}"
          , index, Environment.NewLine);
        gridFunctions.AppendFormat("    U{0}DGF u{0}dgf(u{0}sub,uold);{1}"
          , index, Environment.NewLine);
        gridFunctions.AppendFormat(
          "    pvdwriter.addVertexData(new Dune::PDELab::VTKGridFunctionAdapter<U{0}DGF>(u{0}dgf,\"{1}\"));{2}"
          , index
          , OdeVariables[index]
          , Environment.NewLine);
      }

      map["%DPCREATION%"] = dpCreation.ToString();
      map["%SUBCREATION%"] = subCreation.ToString();
      map["%DPINITIALIZATION%"] = dpInitialization.ToString();
      map["%INITIALTYPECREATION%"] = initialTypeCreation.ToString();
      map["%INITIALTYPES%"] = initialTypes.ToString();
      map["%INITIALARGS%"] = initialArgs.ToString();
      map["%CREATEGRIDFUNCTIONS%"] = gridFunctions.ToString();
      map["%NAME%"] = name;

      builder.AppendLine(ExpandTemplate(Resources.main, map));

      File.WriteAllText(Path.Combine(path, name + ".cc"),
        builder.ToString());
    }

    private static string GenerateGeometryExpressionForAnalyticVolume(AnalyticGeometry vol)
    {
      var volume = vol.getAnalyticVolume(0);
      var builder = new StringBuilder();
      builder.Append("\n");
      builder.AppendFormat("    bool inside={0};\n",
        TranslateExpression(volume.getMath()));
      builder.Append("    return inside;\n");
      return builder.ToString();
    }

    private string WriteFieldToFile(string path, SampledField data, int numSamples1, int numSamples2, int z = 0,
      string basename = "geometry1.dmp")
    {
      var name = Path.Combine(path, basename);
      var length = data.getUncompressedLength();
      var array = new int[length];
      data.getUncompressed(array);

      using (var writer = new StreamWriter(name))
      {
        writer.WriteLine("{0} {1}", numSamples1, numSamples2);
        for (var i = 0; i < numSamples1; ++i)
        {
          for (var j = 0; j < numSamples2; ++j)
          {
            writer.Write(array[i + numSamples1*j + numSamples1*numSamples2*z]);
            writer.Write(" ");
          }
          writer.WriteLine();
        }
      }

      return name;
    }

    private string GenerateExpressionForSampleFieldGeometry(Geometry geometry, SampledFieldGeometry sample, string path)
    {
      var field = geometry.getSampledField(sample.getSampledField());
      var data = field;

      GeometryFile = WriteFieldToFile(path,
        data,
        field.getNumSamples1(),
        field.getNumSamples2());

      var min = sample.getSampledVolume(0).getMinValue();
      var max = sample.getSampledVolume(0).getMaxValue();

      GeometryMin = min;
      GeometryMax = max;

      return "";
    }

    private string GenerateGeometryExpression(string path)
    {
      // by default return the full area
      var result = "return true;";
      // string result = GenerateFish();   // or the fish

      if (SpatialModelPlugin == null) return result;

      if (_Geometry == null || _Geometry.getNumGeometryDefinitions() == 0) return result;

      var vol = _Geometry.GetFirstAnalyticGeometry();
      if (vol != null && vol.getNumAnalyticVolumes() > 0)
        return GenerateGeometryExpressionForAnalyticVolume(vol);

      var sample = _Geometry.GetFirstSampledFieldGeometry();
      if (sample != null && sample.getNumSampledVolumes() > 0)
        return GenerateExpressionForSampleFieldGeometry(_Geometry, sample, path);

      return result;
    }

    private static string GenerateFish()
    {
      return
        "    const auto& x = point[0];\n" +
        "    const auto& y = point[1];\n" +
        "\n" +
        "    bool inside= (pow(0.27 * (-42. + x), 2.) + pow(0.4 * (-50. + y), 2.) < 100. ||\n" +
        "            (y < (-10.+ x) &&  y > (110. + -x) && x < 90.));\n" +
        "\n" +
        "    return inside;\n";
    }

    private void WriteLocalOperator(string path)
    {
      File.WriteAllText(Path.Combine(path, "local_operator.hh"),
        ExpandTemplate(Resources.local_operator));
    }

    private void WriteInitialConditions(string path)
    {
      var builder = new StringBuilder();
      builder.AppendLine("#ifndef INITIAL_CONDITIONS_H");
      builder.AppendLine("#define INITIAL_CONDITIONS_H");
      builder.AppendLine();
      builder.AppendLine("#include <dune/copasi/utilities/datahelper.hh>");
      builder.AppendLine();
      var map = new Dictionary<string, string>();

      var initializer = new StringBuilder();
      var declarations = new StringBuilder();

      foreach (var pItem in ParameterIds)
      {
        initializer.AppendFormat(
          "	   , {0}(param.sub(\"Reaction\").template get<RF>(\"{0}\")){1}"
          , pItem
          , Environment.NewLine
          );
        declarations.AppendFormat(
          "    RF {0};{1}"
          , pItem
          , Environment.NewLine
          );
      }

      var varMap
        = new Dictionary<string, string>
        {
          {"x", "x[0]"},
          {"y", "x[1]"},
          {"z", "x[2]"}
        };


      var additionalInitializations = new StringBuilder();

      var tempBuilder = new StringBuilder();

      var numCompartments = _Model.getNumCompartments();
      if (numCompartments > 1)
      {
        additionalInitializations.AppendLine("      //// determine whether current location is in a compartment");
        for (var i = 0; i < numCompartments; ++i)
        {
          var comp = _Model.getCompartment(i);
          var compId = comp.getId();
          initializer.AppendFormat(
            "	   , dh_in{0}(DataHelper::forFile(param.sub(\"Reaction\").template get<std::string>(\"in_{0}\", \"\"), NearestNeighbor)){1}"
            , compId
            , Environment.NewLine
            );
          declarations.AppendFormat(
            "    const DataHelper* dh_in{0};{1}"
            , compId
            , Environment.NewLine
            );
          additionalInitializations.AppendFormat(
            "      RF isIn_{0} = dh_in{0} == NULL ? 1 :(RF)dh_in{0}->get((double)x[0], (double)x[1]);{1}", compId,
            Environment.NewLine);
        }
      }


      var count = 0;
      foreach (var item in OdeVariables)
      {
        initializer.AppendFormat(
          "	   , dh_{0}(DataHelper::forFile(param.sub(\"Data\").template get<std::string>(\"{0}\"))){1}"
          , item
          , Environment.NewLine
          );

        declarations.AppendFormat(
          "    const DataHelper* dh_{0};{1}"
          , item
          , Environment.NewLine
          );

        tempBuilder.AppendLine();
        tempBuilder.AppendFormat("        case {0}:{1}", count, Environment.NewLine);
        tempBuilder.AppendLine("        {");
        tempBuilder.AppendFormat("          // initial conditions for {0}{1}", item, Environment.NewLine);

        tempBuilder.AppendFormat("          if(dh_{0} != NULL){1}", item, Environment.NewLine);
        tempBuilder.AppendFormat(
          "            __initial  = (typename Traits::RangeType)(*dh_{0}).get((double)(x[0]),(double)x[1] );{1}", item,
          Environment.NewLine);
        tempBuilder.AppendLine("          else");


        tempBuilder.Append("            __initial = ");

        var species = _Model.getSpecies(item);
        if (numCompartments > 1 && species != null && species.isSetCompartment())
        {
          tempBuilder.AppendFormat("isIn_{0} * ", species.getCompartment());
        }

        var initial = _Model.getInitialAssignment(item);
        if (initial != null && initial.isSetMath())
        {
          tempBuilder.AppendFormat(
            "{0};{1}",
            TranslateExpression(initial.getMath(),
              varMap, _Model
              ),
            Environment.NewLine
            );
          tempBuilder.AppendLine();
        }
        else
        {
          if (species != null)
          {
            if (species.isSetInitialConcentration())
              tempBuilder.AppendFormat(
                "{0};{1}"
                , species.getInitialConcentration().ToString("E17")
                , Environment.NewLine);
            else
              tempBuilder.AppendFormat(
                "{0};{1}"
                , species.getInitialAmount().ToString("E17")
                , Environment.NewLine);
          }
        }
        tempBuilder.AppendLine("          break;");
        tempBuilder.AppendLine("        }");

        ++count;
      }


      map["%INITIALIZER%"] = initializer.ToString();
      map["%DECLARATION%"] = declarations.ToString();
      map["%INITIALCONDITION%"] = tempBuilder.ToString();
      map["%ADDITIONAL_INITIAlIZATION%"] = additionalInitializations.ToString();

      builder.AppendLine(ExpandTemplate(Resources.initial_conditions, map));
      builder.AppendLine();

      builder.AppendLine("#endif // INITIAL_CONDITIONS_H");

      File.WriteAllText(Path.Combine(path, "initial_conditions.hh"),
        builder.ToString());
    }

    private void WriteComponentParameters(string path)
    {
      File.WriteAllText(Path.Combine(path, "componentparameters.hh"),
        ExpandTemplate(Resources.componentparameters));
    }

    private void WriteCMakeLists(string path, string name)
    {
      File.WriteAllText(Path.Combine(path, "CMakeLists.txt"),
        ExpandTemplate(Resources.CMakeLists,
          new Dictionary<string, string>
          {
            {"%NAME%", name}
          }
          ));
    }

    private string ExpandTemplate(string content,
      Dictionary<string, string> map = null)
    {
      var final = content;

      if (map != null)
        foreach (var pair in map)
        {
          final = final.Replace(pair.Key, pair.Value);
        }

      return final;
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlDomains.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlDomains : BaseSpatialControl
  {
    public ControlDomains()
    {
      InitializeComponent();
    }

    private Geometry Current { get; set; }

    public void InitializeFrom(Geometry geometry)
    {
      grid.Rows.Clear();
      Current = geometry;
      if (geometry == null) return;

      for (long i = 0; i < geometry.getNumDomains(); ++i)
      {
        var domain = geometry.getDomain(i);
        var domainType = domain.getDomainType();
        var spatialId = domain.getId();
        var builder = new StringBuilder();
        for (long j = 0; j < domain.getNumInteriorPoints(); ++j)
        {
          var current = domain.getInteriorPoint(j);
          builder.Append("(");
          builder.Append(current.getCoord1());
          builder.Append(";");
          builder.Append(current.getCoord2());
          builder.Append(";");
          builder.Append(current.getCoord3());
          builder.Append(")");
          if (j + 1 < domain.getNumInteriorPoints())
            builder.Append(", ");
        }
        grid.Rows.Add(spatialId, domainType, builder.ToString());
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;

      for (var i = 0; i < grid.Rows.Count && i < Current.getNumDomains(); ++i)
      {
        var current = Current.getDomain(i);
        var row = grid.Rows[i];
        current.setId((string) row.Cells[0].Value);
        current.setDomainType((string) row.Cells[1].Value);

        var points = GetPoints((string) row.Cells[2].Value);
        for (var j = 0; j < points.Count && j < current.getNumInteriorPoints(); ++j)
        {
          var interior = current.getInteriorPoint(j);
          var point = points[j];
          interior.setCoord1(point.Item1);
          interior.setCoord2(point.Item2);
          interior.setCoord3(point.Item3);
        }
      }
      OnModelChanged();
    }

    private List<Tuple<double, double, double>> GetPoints(string value)
    {
      value = value.Replace("(", "");
      value = value.Replace(")", "");
      var points = value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
      return (from point in points
        select point.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)
        into coords
        where coords.Length == 3
        select
          new Tuple<double, double, double>
            (Util.SaveDouble(coords[0]),
              Util.SaveDouble(coords[1]),
              Util.SaveDouble(coords[2])))
        .ToList();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }
  }
}<file_sep>/LibEditSpatial/Dialogs/DlgRun.cs
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Dialogs
{
  public partial class DlgRun : Form
  {
    private bool closing;
    private Process process;
    private FileSystemWatcher watcher;

    public DlgRun()
    {
      InitializeComponent();
      closing = false;
      CygwinDir = @"C:\cygwin\bin";
      ParaViewDir = @"E:\Program Files (x86)\ParaView 4.2.0\bin\";
    }

    public string CygwinDir { get; set; }
    public string ParaViewDir { get; set; }
    public DuneConfig Config { get; set; }

    public string FileName
    {
      get { return txtFileName.Text; }
      set { txtFileName.Text = value; }
    }

    private List<Tuple<string, string>> UpdateItems { get; set; }

    private void OnKillClick(object sender, EventArgs e)
    {
      if (process == null) return;
      try
      {
        process.Kill();
      }
      catch
      {
      }
    }

    private void OnAddString(string data)
    {
      if (closing) return;
      if (string.IsNullOrEmpty(data)) return;

      if (InvokeRequired)
      {
        Invoke(new VoidStringDelegate(OnAddString), data);
        return;
      }
      txtResult.Text += data.Replace("\n", Environment.NewLine);
    }

    private void ClearWatcher()
    {
      if (watcher != null)
      {
        watcher.EnableRaisingEvents = false;
        watcher.Dispose();
        watcher = null;
      }
    }

    private void ReEnableUI()
    {
      if (closing) return;
      if (InvokeRequired)
      {
        Invoke(new VoidDelegate(ReEnableUI));
        return;
      }
      progressBar1.Style = ProgressBarStyle.Continuous;
      cmdRun.Enabled = true;
      cmdCompile.Enabled = true;
      cmdKill.Enabled = false;

      ClearWatcher();

      process = null;
    }

    private bool CheckIfEmpty(string dir)
    {
      var outdir = Path.Combine(dir, "vtk");
      if (!Directory.Exists(outdir)) return false;
      var files = Directory.GetFiles(outdir, "*.vt?", SearchOption.TopDirectoryOnly);
      if (files.Length == 0) return false;

      var result =
        MessageBox.Show("There are already results present from a previous simulation, shall I move them away?",
          "Move Existing results?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
      if (result == DialogResult.Yes)
      {
        var now = DateTime.Now;
        var newDir = Path.Combine(dir, now.ToString("yyyy-MM-dd_-_HH-mm-ss") + "_old_results");
        Directory.CreateDirectory(newDir);
        Directory.Move(outdir, Path.Combine(newDir, "vtk"));
        files = Directory.GetFiles(dir, "*.pvd");
        foreach (var item in files)
        {
          var name = Path.GetFileName(item);
          if (name == null) continue;
          File.Move(item, Path.Combine(newDir, name));
        }
      }

      if (result == DialogResult.Cancel)
        return true;

      return false;
    }

    private void OnRunClick(object sender, EventArgs e)
    {
      if (process != null) return;

      var dir = Path.GetDirectoryName(FileName);
      if (dir == null) return;

      var outdir = Path.Combine(dir, "vtk");

      if (CheckIfEmpty(dir)) return;

      txtResult.Text = "Starting with config: temp.conf";

      if (!Directory.Exists(outdir))
        Directory.CreateDirectory(outdir);
      var config = Path.Combine(dir, "temp.conf");
      Config.SaveAs(config);

      var info = new ProcessStartInfo
      {
        FileName = FileName,
        Arguments = Util.ToCygwin(config),
        WorkingDirectory = dir,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        CreateNoWindow = true
      };      

#pragma warning disable 4014
      StartProcess(info);
#pragma warning restore 4014
    }

    private void QueueUpdate(string fullPath, string selectedItem)
    {
      UpdateItems.Add(new Tuple<string, string>(fullPath, selectedItem));

      LaunchUpdate();
    }

    private void UpdateWorkerDoWork(object sender, DoWorkEventArgs e)
    {
      var arg = e.Argument as Tuple<string, string>;
      if (arg == null) return;

      GeneratePreview(arg.Item1, arg.Item2);
    }

    private void LaunchUpdate()
    {
      if (backgroundWorker1.IsBusy) return;

      var last = UpdateItems.LastOrDefault();
      UpdateItems.Clear();

      if (last == null) return;

      backgroundWorker1.RunWorkerAsync(last);
    }

    private void OnNewResult(object sender, FileSystemEventArgs e)
    {
      if (closing) return;
      if (InvokeRequired)
      {
        Invoke(new FileSystemEventHandler(OnNewResult), sender, e);
        return;
      }

      if (!chkPreview.Checked || cmbVariable.Items.Count == 0) return;

      QueueUpdate(e.FullPath, cmbVariable.SelectedItem as string);
    }

    private void UpdatePreview(byte[] bytes, string species)
    {
      if (InvokeRequired)
      {
        Invoke(new VoidByteStringDelegate(UpdatePreview), bytes, species);
        return;
      }
      if (!cmbVariable.SelectedItem.Equals(species))
        return;

      Image image;
      using (var stream = new MemoryStream(bytes))
      {
        image = Image.FromStream(stream);
      }
      pictureBox1.Image = image;
    }

    public void GeneratePreview(string filename, string species)
    {
      if (string.IsNullOrWhiteSpace(species)) return;
      if (!File.Exists(filename)) return;

      Debug.WriteLine("Generating Preview for: {0} from {1}", species, Path.GetFileNameWithoutExtension(filename));

      var tempFile = Path.Combine(Path.GetTempPath(),
        Path.GetRandomFileName() + ".png");

      var info = new ProcessStartInfo
      {
        FileName = Path.Combine(ParaViewDir, "pvpython"),
        Arguments = string.Format("\"{0}\" \"{1}\" {2} \"{3}\"",
          Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "pvrender.py"),
          filename,
          species,
          tempFile
          ),
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardError = true,
        RedirectStandardOutput = true
      };

      var previewProcess = new Process {StartInfo = info};
      previewProcess.OutputDataReceived += (o, e2) => OnAddString(e2.Data);
      previewProcess.ErrorDataReceived += (o, e3) => OnAddString(e3.Data);
      previewProcess.Start();
      previewProcess.EnableRaisingEvents = true;
      previewProcess.BeginOutputReadLine();
      previewProcess.BeginErrorReadLine();
      previewProcess.WaitForExit();
      previewProcess.EnableRaisingEvents = false;

      if (File.Exists(tempFile))
      {
        var bytes = File.ReadAllBytes(tempFile);

        File.Delete(tempFile);

        UpdatePreview(bytes, species);
      }
    }

    public async Task StartProcess(ProcessStartInfo info)
    {
      if (!string.IsNullOrEmpty(CygwinDir))
        info.EnvironmentVariables["PATH"] = string.Format("{0};{1}", CygwinDir, info.EnvironmentVariables["PATH"]);

      process = new Process {StartInfo = info, EnableRaisingEvents = true};
      process.Exited += (o, e1) => ReEnableUI();
      process.OutputDataReceived += (o, e2) => OnAddString(e2.Data);
      process.ErrorDataReceived += (o, e3) => OnAddString(e3.Data);
      process.Start();
      Thread.Sleep(100);
      process.BeginOutputReadLine();
      process.BeginErrorReadLine();
      cmdRun.Enabled = false;
      cmdCompile.Enabled = false;
      cmdKill.Enabled = true;
      progressBar1.Style = ProgressBarStyle.Marquee;

      ClearWatcher();

      watcher = new FileSystemWatcher
      {
        Path = Path.Combine(info.WorkingDirectory, "vtk"),
        IncludeSubdirectories = false,
        Filter = "*.vt?",
        NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
      };

      watcher.Created += OnNewResult;
      watcher.Changed += OnNewResult;
      watcher.EnableRaisingEvents = true;

      await Task.Run(() => process.WaitForExit());
    }

    public static void WriteBatchFiles(string dir, string cygwinDir)
    {
      foreach (var item in new[] { "start.bat", "compile.bat" })
      {
        if (File.Exists(Path.Combine(dir, "start.bat"))) continue;
        
        var batch = File.ReadAllText(
          Path.Combine(
          Path.GetDirectoryName(Application.ExecutablePath), item + ".in"));
        batch = batch.Replace("$$CYG_DIR$$", cygwinDir);
        File.WriteAllText(Path.Combine(dir, "src\\" + item), batch);
      }
    }

    private void OnCompileClick(object sender, EventArgs e)
    {
      if (process != null) return;

      var dir = Path.GetDirectoryName(FileName);

      if (dir == null) return;

      var info = new ProcessStartInfo
      {
        FileName = Path.Combine(CygwinDir, "bash"),
        Arguments = "-c make",
        WorkingDirectory = dir,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        CreateNoWindow = true
      };

#pragma warning disable 4014
      StartProcess(info);
#pragma warning restore 4014

      WriteBatchFiles(dir, CygwinDir);
    }

    private void OnBrowseClick(object sender, EventArgs e)
    {
      using (var dialog = new OpenFileDialog
      {
        FileName = FileName,
        Title = "Select executable",
        Filter = "All files|*.*"
      })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
          FileName = dialog.FileName;
      }
    }

    private void DlgRun_Load(object sender, EventArgs e)
    {
      cmbVariable.Items.Clear();
      if (Config != null)
      {
        foreach (var v in Config.GetVariableKeys())
          cmbVariable.Items.Add(v);
        if (cmbVariable.Items.Count > 0)
        {
          cmbVariable.SelectedIndex = 0;
          chkPreview.Checked = true;
        }
        else
          chkPreview.Checked = false;
      }

      UpdateItems = new List<Tuple<string, string>>();

      if (File.Exists(FileName))
        OnRunClick(sender, e);
    }

    private void DlgRun_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (process != null && !process.HasExited)
      {
        if (
          MessageBox.Show("The simulation is still running, do you want to kill it?", "Kill Simulation?",
            MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
        {
          if (process != null)
          {
            process.Kill();
          }

          ClearWatcher();

          closing = true;
        }
        else e.Cancel = true;
      }
    }

    private void OnOpenPV(object sender, EventArgs e)
    {
      try
      {
        var dir = Path.GetDirectoryName(FileName);
        if (dir == null) return;
        var pvd = Directory.GetFiles(dir, "*.pvd", SearchOption.TopDirectoryOnly);
        if (pvd.Length > 0)
          Process.Start(Path.Combine(ParaViewDir, "paraview"), pvd[0]);
      }
      catch
      {
      }
    }

    private void UpdateWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      LaunchUpdate();
    }

    private void OnFolderClick(object sender, EventArgs e)
    {
      try
      {
        Process.Start(Path.GetDirectoryName(FileName));
      }
      catch
      {
          
      }
    }
  }
}<file_sep>/WFEditSpatial/Program.cs
﻿using System;
using System.IO;
using System.Windows.Forms;
using SBW;

namespace EditSpatial
{
  internal static class Program
  {
    private static object module;

    /// <summary>
    ///   The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      var mainForm = new MainForm();
      try
      {
        module = mainForm;
        DefaultModule.EnableServices(ref module,
          "EditSpatial", // module name
          "Edit Spatial", // display name
          "Creates / Edits spatial models",
          LowLevel.ModuleManagementType.UniqueModule,
          "analysis", // service name
          "Create/Edit Spatial Description", // service display name
          "/Analysis", // category
          "Creates / Edits spatial models", //
          args
          );
      }
      catch
      {
      }

      if (args.Length == 1 && File.Exists(args[0]))
        mainForm.OpenFile(args[0]);

      Application.Run(mainForm);
    }
  }
}<file_sep>/WFEditSpatial/Model/ReactionInfo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using libsbmlcs;

namespace EditSpatial.Model
{
  public class ReactionInfo : IDisposable
  {
    public ReactionInfo(Reaction reaction)
    {
      Reactants = new List<SpeciesReferenceInfo>();
      Products = new List<SpeciesReferenceInfo>();

      Reaction = reaction;
      Model = reaction.getModel();

      AnalyzeReaction();
    }

    public Reaction Reaction { get; set; }
    public libsbmlcs.Model Model { get; set; }
    public List<SpeciesReferenceInfo> Reactants { get; set; }
    public List<SpeciesReferenceInfo> Products { get; set; }

    public List<string> CompartmentIds
    {
      get
      {
        var list = Reactants.Select(sri => sri.Compartment).ToList();
        list.AddRange(Products.Select(sri => sri.Compartment).ToList());
        return list.Distinct().ToList();
      }
    }

    private void AnalyzeReaction()
    {
      for (var i = 0; i < Reaction.getNumReactants(); ++i)
      {
        var reference = Reaction.getReactant(i);
        if (reference != null && reference.isSetSpecies())
        {
          var species = Model.getSpecies(reference.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          Reactants.Add(new SpeciesReferenceInfo
          {
            Id = species.getId(),
            Compartment = species.getCompartment(),
            Stoichiometry = reference.getStoichiometry()
          });
        }
      }

      for (var i = 0; i < Reaction.getNumProducts(); ++i)
      {
        var reference = Reaction.getProduct(i);
        if (reference != null && reference.isSetSpecies())
        {
          var species = Model.getSpecies(reference.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          Products.Add(new SpeciesReferenceInfo
          {
            Id = species.getId(),
            Compartment = species.getCompartment(),
            Stoichiometry = reference.getStoichiometry()
          });
        }
      }
    }

    public void Dispose()
    {
      if (Model != null)
      Model.Dispose();
    }
  }
}
<file_sep>/WFEditSpatial/Controls/ControlDisplayNode.cs
﻿using System;
using System.Windows.Forms;

namespace EditSpatial.Controls
{
  public partial class ControlDisplayNode : BaseSpatialControl
  {
    public ControlDisplayNode()
    {
      InitializeComponent();
      controlText1.WordWrap = true;
      controlText1.ReadOnly = true;
    }

    public void DisplayNode(TreeNode node)
    {
      var sbml = node == null ? null : (string) node.Tag;
      if (string.IsNullOrWhiteSpace(sbml))
        sbml = "<none>";
      sbml = sbml.Replace("\n", Environment.NewLine);
      controlText1.Text = sbml;
    }

    public override void SaveChanges()
    {
      // not supported ... 
    }

    public override void InvalidateSelection()
    {
      DisplayNode(null);
    }
  }
}<file_sep>/WFEditSpatial/Model/ISBWAnalyzer.cs
﻿using SBW;

namespace EditSpatial.Model
{
  internal delegate void doAnalysisDelegate(string model);

  internal interface ISBWAnalyzer
  {
    [Help("Loads a SBML string into EditSpatial")]
    void doAnalysis(string model);
  }
}<file_sep>/WFEditSpatial/Controls/ControlParameters.cs
﻿using System.Collections.Generic;
using System.Windows.Forms;
using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlParameters : BaseSpatialControl
  {
    public ControlParameters()
    {
      InitializeComponent();
      RowsAdded = new List<int>();
    }

    private SBMLDocument Current { get; set; }
    public List<int> RowsAdded { get; set; }

    public void InitializeFrom(SBMLDocument document)
    {
      grid.Rows.Clear();
      Current = document;
      if (document == null || document.getModel() == null) return;
      try
      {
        IsInitializing = true;
        var model = document.getModel();

        CommitAddedRows(Current);
        for (long i = 0; i < model.getNumParameters(); ++i)
        {
          var current = model.getParameter(i);
          grid.Rows.Add(current.getId(),
            current.getName(),
            current.getValue().ToString());
        }
      }
      finally
      {
        IsInitializing = false;
      }
    }

    private void CommitAddedRows(SBMLDocument current)
    {
      if (RowsAdded.Count > 0 && current != null)
      {
        for (var i = RowsAdded.Count - 1; i >= 0; i--)
        {
          try
          {
            var index = RowsAdded[i];
            if (index < 0) continue;
            var row = grid.Rows[index];
            var param = current.getModel().createParameter();
            param.initDefaults();
            param.setId(row.Cells[0].Value as string);
            param.setName(row.Cells[1].Value as string);
            double value;
            if (double.TryParse((string) row.Cells[2].Value, out value))
              param.setValue(value);
            RowsAdded.RemoveAt(i);
          }
          catch
          {
          }
        }
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;

      CommitAddedRows(Current);

      for (var i = 0; i < grid.Rows.Count; ++i)
      {
        var row = grid.Rows[i];
        var current = Current.getModel().getParameter((string) row.Cells[0].Value);
        if (current == null) continue;

        current.setName((string) row.Cells[1].Value);
        double value;
        if (double.TryParse((string) row.Cells[2].Value, out value))
          current.setValue(value);
      }

      OnModelChanged();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }

    private void OnUserDeletedRow(object sender, DataGridViewRowEventArgs e)
    {
      if (Current == null) return;

      var ids = new List<string>();
      if (grid.SelectedRows.Count > 1)
      {
        foreach (DataGridViewRow row in grid.SelectedRows)
        {
          ids.Add(row.Cells[0].Value as string);
        }
      }
      else
      {
        ids.Add(e.Row.Cells[0].Value as string);
      }

      foreach (var id in ids)
        Current.getModel().removeParameter(id);

      InitializeFrom(Current);
    }

    private void OnRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
    {
      if (Current == null || IsInitializing || e.RowIndex == 0) return;

      RowsAdded.Add(e.RowIndex - 1);
    }
  }
}<file_sep>/WFEditSpatial/Model/CustomSpatialValidator.cs
using libsbmlcs;

namespace EditSpatial.Model
{
  public class CustomSpatialValidator : SBMLValidator
  {
    public CustomSpatialValidator()
    {
    }

    public CustomSpatialValidator(CustomSpatialValidator orig)
      : base(orig)
    {
    }

    public override SBMLValidator clone()
    {
      return new CustomSpatialValidator(this);
    }

    private bool containsSpatialMath(ASTNode node, libsbmlcs.Model model)
    {
      for (var i = 0; i < node.getNumChildren(); ++i)
      {
        if (containsSpatialMath(node.getChild(i), model))
          return true;
      }

      if (!node.isName()) return false;

      var element = model.getElementBySId(node.getName());

      if (element == null) return false;

      if (element.getTypeCode() == libsbml.SBML_SPECIES)
      {
        var plug = (SpatialSpeciesPlugin) (element.getPlugin("spatial"));
        if (plug == null) return false;
        return plug.getIsSpatial();
      }

      return false;
    }

    private bool dependencesAreSpatial(SBase s, libsbmlcs.Model model)
    {
      if (model == null || s == null || !s.isSetId()) return false;

      var rule = model.getRule(s.getId());
      if (rule == null || rule.getTypeCode() != libsbml.SBML_ASSIGNMENT_RULE) return false;

      var ar = (AssignmentRule) (rule);
      if (!ar.isSetMath()) return false;

      return containsSpatialMath(ar.getMath(), model);
    }

    public override long validate()
    {
      // if we don't have a model we don't apply this validator.
      if (getDocument() == null || getModel() == null)
        return 0;

      var numErrors = 0;

      var model = getModel();
      for (var i = 0; i < model.getNumSpecies(); ++i)
      {
        var species = model.getSpecies(i);
        var plug = (SpatialSpeciesPlugin) species.getPlugin("spatial");
        if (plug == null) continue;

        if (plug.isSetIsSpatial() && plug.getIsSpatial()) continue;

        if (dependencesAreSpatial(species, model))
        {
          numErrors++;
          getErrorLog().add(new SBMLError(99999, 3, 1,
            string.Format(
              "This model has a non-spatial species, with rules involving spatial species. Please change the flag of '{0}'",
              species.getId()),
            0, 0,
            libsbml.LIBSBML_SEV_ERROR, // or LIBSBML_SEV_ERROR if you want to stop
            libsbml.LIBSBML_CAT_SBML // or whatever category you prefer
            ));
        }
      }
      return numErrors;
    }
  }
}<file_sep>/WFEditSpatial/Templates/CMakeLists.txt

set (HEADERS 

  local_operator.hh 
  componentparameters.hh 
  initial_conditions.hh 
  reactionadapter.hh
)

set (SOURCES 
  %NAME%.cc
)

add_executable(%NAME% ${SOURCES})
target_link_dune_default_libraries("%NAME%")

# copy config file if we don't have it
if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/%NAME%.conf")
  configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/%NAME%.conf 
    ${CMAKE_CURRENT_BINARY_DIR}/%NAME%.conf COPYONLY)
endif()

# copy sbml file if we don't have it
if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/sbml.xml")
  configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/sbml.xml 
    ${CMAKE_CURRENT_BINARY_DIR}/sbml.xml COPYONLY)
endif()


# create vtk dir, as otherwise the executable just falls over
if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/vtk")
  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/vtk")
endif()
<file_sep>/LibEditSpatial/Controls/CtrlDomain.cs
﻿using System;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Controls
{
  public partial class CtrlDomain : UserControl
  {
    public CtrlDomain()
    {
      InitializeComponent();
    }

    public DomainConfig Model { get; set; }

    public void LoadModel(DomainConfig model)
    {
      Model = model;

      txtWidth.Text = Model.Width.ToString(CultureInfo.InvariantCulture);
      txtHeight.Text = Model.Height.ToString(CultureInfo.InvariantCulture);
      txtDepth.Text = Model.Depth.ToString(CultureInfo.InvariantCulture);

      txtGridX.Text = Model.GridX.ToString();
      txtGridY.Text = Model.GridY.ToString();
      txtGridZ.Text = Model.GridZ.ToString();
      txtRefinement.Text = Model.Refinement.ToString();

      txtFile.Text = Model.Geometry;
      txtMin.Text = Model.GeometryMin.ToString(CultureInfo.InvariantCulture);
      txtMax.Text = Model.GeometryMax.ToString(CultureInfo.InvariantCulture);
    }

    private void OnWidthChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtWidth.Text, out temp))
        Model.Width = temp;
    }

    private void OnHeightChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtHeight.Text, out temp))
        Model.Height = temp;
    }

    private void OnDepthChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtDepth.Text, out temp))
        Model.Depth = temp;
    }

    private void OnGridXChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtGridX.Text, out temp))
        Model.GridX = temp;
    }

    private void OnGridYChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtGridY.Text, out temp))
        Model.GridY = temp;
    }

    private void OnGridZChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtGridZ.Text, out temp))
        Model.GridZ = temp;
    }

    private void OnRefinementChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtRefinement.Text, out temp))
        Model.Refinement = temp;
    }

    private void OnFileChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.Geometry = txtFile.Text;
    }

    private void OnMinChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtMin.Text, out temp))
        Model.GeometryMin = temp;
    }

    private void OnMaxChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtMax.Text, out temp))
        Model.GeometryMax = temp;
    }

    private void OnLoadClicked(object sender, EventArgs e)
    {
      if (Model == null) return;
      OnBrowseDmpFile();
    }

    public event EventHandler<string> OpenDmpFile;
    public event EventHandler BrowseDmpFile;

    protected virtual void OnBrowseDmpFile()
    {
      var handler = BrowseDmpFile;
      if (handler != null) handler(this, EventArgs.Empty);
    }

    protected virtual void OnOpenDmpFile(string filename)
    {
      var handler = OpenDmpFile;
      if (handler != null) handler(this, filename);
    }

    private void OnEditClicked(object sender, EventArgs e)
    {
      if (Model == null) return;
      var file = Model.Geometry;
      if (!File.Exists(file)) return;
      try
      {
        OnOpenDmpFile(file);
      }
      catch
      {
      }
    }
  }
}<file_sep>/LibEditSpatial/Model/DmpModel.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace LibEditSpatial.Model
{
  public class DmpModel : ICloneable
  {
    public DmpModel(int cols, int rows)
    {
      Palette = DmpPalette.Default;

      Columns = cols;
      MaxX = cols;
      Rows = rows;
      MaxY = rows;
      Data = new double[cols, rows];
      Min = 0;
      Max = 10;
    }

    /// <summary>
    /// Flag indicating whether the current model needs to be saved
    /// </summary>
    public bool Dirty { get; set; }

    /// <summary>
    /// Current Palette
    /// </summary>
    public DmpPalette Palette { get; set; }
  
    /// <summary>
    /// Filename for the model
    /// </summary>
    public string FileName { get; set; }
    
    /// <summary>
    /// Number of Columns
    /// </summary>
    public int Columns { get; set; }

    /// <summary>
    /// Returns all unique values in this model
    /// </summary>
    public List<double> Range
    {
      get
      {
        var numbers = Data;
        var unique = Enumerable.Range(0, numbers.GetUpperBound(0) + 1)
          .SelectMany(x => Enumerable.Range(0, numbers.GetUpperBound(1) + 1)
            .Select(y => numbers[x, y])).Distinct().ToList();
        unique.Sort();
        return unique;
      }
    }

    /// <summary>
    /// X range
    /// </summary>
    public double XRange {
      get { return MaxX - MinX; }
    }

    /// <summary>
    /// Y range
    /// </summary>
    public double YRange
    {
      get { return MaxY - MinY; }
    }

    /// <summary>
    /// data range
    /// </summary>
    public double DataRange
    {
      get { return Max - Min; }
    }
  
    /// <summary>
    /// number of rows
    /// </summary>
    public int Rows { get; set; }

    /// <summary>
    ///   Saves data in form of [columns, rows]
    /// </summary>
    public double[,] Data { get; set; }

    public double MinX { get; set; }
    public double MaxX { get; set; }
    public double MinY { get; set; }
    public double MaxY { get; set; }
    public double Min { get; set; }
    public double Max { get; set; }
    public bool IssueEvents { get; set; }

    /// <summary>
    ///   Access the element at the given index position
    /// </summary>
    /// <param name="x">index from 0...Columns</param>
    /// <param name="y">index from 0...Rows</param>
    /// <returns>value at given index position</returns>
    public double this[int x, int y]
    {
      get { return Data[x, y]; }
      set
      {
        Data[x, y] = value;
        Min = Math.Min(Min, value);
        Max = Math.Max(Max, value);
        OnModelChanged();
      }
    }

    public object Clone()
    {
      return new DmpModel(Columns, Rows)
      {
        MinX = MinX,
        MaxX = MaxX,
        MinY = MinY,
        MaxY = MaxY,
        Min = Min,
        Max = Max,
        Data = (double[,]) Data.Clone()
      };
    }

    //public double this[double x, double y]
    //{
    //  get
    //  {
    //    if (x < MinX)
    //      x = MinX;
    //    if (x > MaxX)
    //      x = MaxX;
    //    if (y < MinY)
    //      y = MinY;
    //    if (y > MaxY)
    //      y = MaxY;

    //    int posX = (int)Math.Round((x - MinX)) ;
    //    int posY = 0;


    //    return Data[posX, posY]; 
    //  }
    //  set
    //  {
    //    Data[x, y] = value;
    //    Min = Math.Min(Min, value);
    //    Max = Math.Max(Max, value);
    //    OnModelChanged();
    //  }
    //}

    public event EventHandler<DmpModel> ModelChanged;

    public void Invert()
    {
      var range = Range;
      var count = range.Count/2;
      for (var i = 0; i < count; i++)
      {
        var first = range[i];
        var last = range[range.Count - 1 - i];
        Transform(
          x =>
          {
            if (Math.Abs(x - first) < 1e-10) return last;
            if (Math.Abs(x - last) < 1e-10) return first;
            return x;
          }
          );
      }
    }

    public void OnModelChanged()
    {
      Dirty = true;
      if (ModelChanged != null && IssueEvents)
      {
        ModelChanged(this, this);
      }
    }

    public void RotateRight()
    {
      Data = RotateMatrixCounterClockwise(Data);
      Columns = Data.GetLength(0);
      Rows = Data.GetLength(1);
    }

    private static T[,] RotateMatrixCounterClockwise<T>(T[,] oldMatrix)
    {
      var newMatrix = new T[oldMatrix.GetLength(1), oldMatrix.GetLength(0)];
      var newRow = 0;
      for (var oldColumn = oldMatrix.GetLength(1) - 1; oldColumn >= 0; oldColumn--)
      {
        var newColumn = 0;
        for (var oldRow = 0; oldRow < oldMatrix.GetLength(0); oldRow++)
        {
          newMatrix[newRow, newColumn] = oldMatrix[oldRow, oldColumn];
          newColumn++;
        }
        newRow++;
      }
      return newMatrix;
    }

    protected T[,] ResizeArray<T>(T[,] original, int x, int y)
    {
      var newArray = new T[x, y];
      var minX = Math.Min(original.GetLength(0), newArray.GetLength(0));
      var minY = Math.Min(original.GetLength(1), newArray.GetLength(1));

      for (var i = 0; i < minY; ++i)
        for (var j = 0; j < minX; ++j)
          newArray[j, i] = original[j, i];

      return newArray;
    }

    public void Resize(int cols, int rows, bool scaleExisting = false)
    {
      if (scaleExisting)
      { 
        ResizeByScaling(cols, rows);
        return;
      }

      Data = ResizeArray(Data, cols, rows);
      Rows = rows;
      Columns = cols;
      Dirty = true;
    }

    private static DmpPalette GetRedPaletteOrDefault()
    {
      try
      {
        string file = Path.Combine(
                            Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
                            "Palettes/black-red.txt");
        if (File.Exists(file))
          return DmpPalette.FromFile(
                        file
                        );
      }
      catch
      {

      }
      return DmpPalette.Default;
    }

    private void ResizeByScalingWithPalette(int cols, int rows, DmpPalette palette)
    {
      var image = ToImage(palette);
      var bitmap = new Bitmap(cols, rows);
      using (var g = Graphics.FromImage(bitmap))
      {
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
        g.DrawImage(image, 0, 0, cols, rows);
        g.Flush(System.Drawing.Drawing2D.FlushIntention.Flush);
      }

      var dmp = FromImage(bitmap, (c) =>
      {
        var index = palette.GetIndex(c);
        if (index == -1)
          return 0;
        return MapFromUnit((double)index / (double)palette.Colors.Count);
      });

      Data = dmp.Data;
      Columns = dmp.Columns;
      Rows = dmp.Rows;
    }
    /// <summary>
    /// Creates a new image with required resolution, 
    /// and samples it again. 
    /// </summary>
    /// <param name="cols"></param>
    /// <param name="rows"></param>
    public void ResizeByScaling(int cols, int rows)
    {
      DmpPalette palette = GetRedPaletteOrDefault();
      ResizeByScalingWithPalette(cols, rows, palette);

    }

    public void SaveAs(string fileName)
    {
      var builder = new StringBuilder();

      builder.AppendFormat("{0} {1}{2}", Columns, Rows, Environment.NewLine);
      builder.AppendFormat("{0} {1} {2} {3} {4}", MinX, MaxX, MinY, MaxY, Environment.NewLine);

      for (var y = 0; y < Rows; y++)
      {
        for (var x = 0; x < Columns; x++)

        {
          builder.Append(Data[x, y]);
          if (x + 1 < Columns)
            builder.Append(" ");
        }
        builder.AppendLine();
      }

      File.WriteAllText(fileName, builder.ToString());
      FileName = fileName;
      Dirty = false;
    }

    public Color GetColor(double val)
    {
      return Palette.GetColor(MapToUnit(val));
    }

    public Bitmap ToImage(DmpPalette palette)
    {
      if (Columns == 0 && Rows == 0)
        return new Bitmap(1, 1);

      var result = new Bitmap(Columns, Rows);
      for (var x = 0; x < Columns; x++)
        for (var y = 0; y < Rows; y++)
          result.SetPixel(x, y, palette.GetColor(MapToUnit(Data[x, y])));

      return result;
    }

    public Bitmap ToImage()
    {
      if (Columns == 0 && Rows == 0)
        return new Bitmap(1, 1);

      var result = new Bitmap(Columns, Rows);
      for (var x = 0; x < Columns; x++)
        for (var y = 0; y < Rows; y++)
          result.SetPixel(x, y, GetColor(Data[x, y]));

      return result;
    }

    public static bool IsImageFile(string fileName)
    {
      var ext = Path.GetExtension(fileName).ToLowerInvariant();
      return ext.EndsWith("tif") || ext.EndsWith("tiff") || ext.EndsWith("png") || ext.EndsWith("jpg") ||
             ext.EndsWith("jpeg") || ext.EndsWith("bmp");
    }

    public static DmpModel FromFile(string filename)
    {
      if (IsImageFile(filename))
        return FromImage(filename);          


      var lines = File.ReadAllLines(filename);
      var rows = 0;
      var cols = 0;
      if (lines.Length > 0)
      {
        var dims = lines[0].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
        if (dims.Length > 1)
        {
          int.TryParse(dims[0], out cols);
          int.TryParse(dims[1], out rows);
        }
      }

      var model = new DmpModel(cols, rows)
      {
        Min = double.MaxValue,
        Max = double.MinValue,
        FileName = filename
      };

      var start = 2;
      if (lines.Length > 1)
      {
        var dims = lines[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
        if (dims.Length == 4)
        {
          double temp;
          if (double.TryParse(dims[0], out temp))
            model.MinX = temp;
          if (double.TryParse(dims[1], out temp))
            model.MaxX = temp;
          if (double.TryParse(dims[2], out temp))
            model.MinY = temp;
          if (double.TryParse(dims[3], out temp))
            model.MaxY = temp;
        }
        else
        {
          // old format
          model.MinX = 0;
          model.MinY = 0;
          model.MaxX = model.Columns;
          model.MaxY = model.Rows;

          start = 1;
        }
      }

      for (var y = start; y < Math.Min(rows + start, lines.Length); y++)
      {
        var entries = lines[y].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
        for (var x = 0; x < Math.Min(cols, entries.Length); x++)
        {
          double current;
          if (double.TryParse(entries[x], out current))
          {
            model[x, y - start] = current;
          }
        }
      }

      if (Math.Abs(model.Min - model.Max) < 1e-10)
      {
        model.Max = model.Min + 10;
      }

      model.Dirty = false;
      return model;
    }

    public static DmpModel FromImage(Bitmap bmp, Func<Color,double> mapper = null)
    {
      if (bmp == null) return null;
      //var image = mapper == null ? ToGrayScale(bmp) : bmp;
      var image =  bmp;
      var dmp = new DmpModel(image.Width, image.Height);
      for (var y = 0; y < dmp.Rows; ++y)
        for (var x = 0; x < dmp.Columns; ++x)
          if (mapper == null)
            dmp[x, y] = image.GetPixel(x, y).R;
          else
            dmp[x, y] = mapper(image.GetPixel(x, y));
      dmp.Dirty = false;
      return dmp;
    }

    public static DmpModel FromImage(string filename)
    {
      var bmp = Image.FromFile(filename) as Bitmap;
      return FromImage(bmp);
    }

    public double MapToUnit(double value)
    {
      if (value < Min) return 0;
      if (value > Max) return 1;

      if (Math.Abs(DataRange) < 1e-10) return 0;

      return (value-Min)/DataRange;
    }

    public double MapFromUnit(double value)
    {
      return value*DataRange + Min;
    }

    private Rectangle FindDimensions()
    {
      var minX = Columns;
      var maxX = 0;
      var minY = Rows;
      var maxY = 0;

      for (var y = 0; y < Rows; ++y)
        for (var x = 0; x < Columns; ++x)
        {
          var current = Data[x, y];
          if (Math.Abs(current) > 1e-10)
          {
            if (x < minX)
              minX = x;
            if (x > maxX)
              maxX = x;
            if (y < minY)
              minY = y;
            if (y > maxY)
              maxY = y;
          }
        }

      return new Rectangle(minX, minY, maxX, maxY);
    }

    public void Center()
    {
      // find dimensions of non-zero entries
      var used = FindDimensions();
      //Data[used.Y, used.X] = 3;
      //Data[used.Height, used.Width] = 5;
      var total = new Rectangle(0, 0, Columns, Rows);
      var endDiff = new Size(total.Width - used.Width, total.Height - used.Height);
      var newStart = new Size(
        (int) Math.Floor((endDiff.Width + used.X)/2.0),
        (int) Math.Floor((endDiff.Height + used.Y)/2.0));
      //Data[newStart.Height, newStart.Width] = 7;
      var difference = new Size(newStart.Width - used.X, newStart.Height - used.Y);
      // Data[difference.Width, difference.Height] = 7;
      var data = new double[Columns, Rows];
      for (var y = used.Y; y <= used.Height; y++)
        for (var x = used.X; x <= used.Width; x++)
          data[x + difference.Width, y + difference.Height] = Data[x, y];
      Data = data;
    }

    public void Transform(Func<double, double> transformation)
    {
      if (transformation == null) return;

      for (var y = 0; y < Rows; ++y)
        for (var x = 0; x < Columns; ++x)
          Data[x, y] = transformation(Data[x, y]);
    }

    public static DmpModel operator +(DmpModel model, double value)
    {
      var tmp = (DmpModel) model.Clone();
      tmp.Transform(x => x + value);
      return tmp;
    }

    public static DmpModel operator +(DmpModel model, DmpModel other)
    {
      var tmp = (DmpModel)model.Clone();
      tmp.Resize(other.Columns, other.Rows);

      for (var y = 0; y < other.Rows; ++y)
        for (var x = 0; x < other.Columns; ++x)
          tmp[x, y] = tmp[x, y] + other[x, y];
      
      return tmp;
    }

    public static DmpModel operator *(DmpModel model, double value)
    {
      var tmp = (DmpModel) model.Clone();
      tmp.Transform(x => x*value);
      return tmp;
    }

    public void MaskWith(DmpModel file)
    {
      file.Resize(Columns, Rows);

      for (var y = 0; y < Rows; ++y)
        for (var x = 0; x < Columns; ++x)
          this[x, y] = file[x,y] * Data[x, y];      
    }


    /// <summary>
    /// Convert image to grayscale, from: 
    /// 
    /// http://tech.pro/tutorial/660/csharp-tutorial-convert-a-color-image-to-grayscale
    /// 
    /// </summary>
    /// <param name="original"></param>
    /// <returns></returns>
    public static Bitmap ToGrayScale(Bitmap original)
    {
      //create a blank bitmap the same size as original
      Bitmap newBitmap = new Bitmap(original.Width, original.Height);

      //get a graphics object from the new image
      Graphics g = Graphics.FromImage(newBitmap);

      //create the grayscale ColorMatrix
      ColorMatrix colorMatrix = new ColorMatrix(
         new float[][] 
      {
         new float[] {.3f, .3f, .3f, 0, 0},
         new float[] {.59f, .59f, .59f, 0, 0},
         new float[] {.11f, .11f, .11f, 0, 0},
         new float[] {0, 0, 0, 1, 0},
         new float[] {0, 0, 0, 0, 1}
      });

      //create some image attributes
      ImageAttributes attributes = new ImageAttributes();

      //set the color matrix attribute
      attributes.SetColorMatrix(colorMatrix);

      //draw the original image on the new image
      //using the grayscale color matrix
      g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
         0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

      //dispose the Graphics object
      g.Dispose();
      return newBitmap;
    }
  }
}
<file_sep>/WFEditSpatial/Forms/FormErrors.cs
﻿using System;
using System.Windows.Forms;
using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Forms
{
  public partial class FormErrors : Form
  {
    public FormErrors()
    {
      InitializeComponent();
    }

    private SpatialModel Model { get; set; }

    public string Label
    {
      get { return label1.Text; }
      set { label1.Text = value; }
    }

    private void OnValidateClick(object sender, EventArgs e)
    {
      ReValidate();
    }

    private void ReValidate()
    {
      if (Model == null || Model.Document == null) return;
      Model.Document.clearValidators();
      Model.Document.addValidator(SpatialModel.CustomSpatialValidator);
      Model.Document.validateSBML();
      InitializeFrom(Model);
    }

    public void InitializeFrom(SpatialModel model)
    {
      Model = model;

      var haveNoDocument = model == null || model.Document == null;

      var errors = haveNoDocument
        ? "No document loaded ..."
        : model.Document.getErrorLog().toString().Replace("\n", Environment.NewLine);
      controlText1.Text = errors;

      if (haveNoDocument)
      {
        label1.Text = "";
        return;
      }

      var numWarnings = model.Document.getNumErrors(libsbml.LIBSBML_SEV_WARNING);
      var numErrors = model.Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR);
      var numFatals = model.Document.getNumErrors(libsbml.LIBSBML_SEV_FATAL);

      if (numErrors + numFatals > 0)
      {
        label1.Text = "The document is invalid SBML.";
      }
      else if (numWarnings > 0)
      {
        label1.Text = "The document is valid SBML, but there were warnings.";
      }
      else
      {
        label1.Text = "The document is valid SBML.";
      }

      label1.Text += string.Format(" Warning(s): {0}, Error(s): {1}, Fatal Error(s): {2}", numWarnings, numErrors,
        numFatals);
    }

    private void OnCloseClick(object sender, EventArgs e)
    {
      Hide();
    }

    private void OnFixCommonErrors(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.FixCommonErrors();
      ReValidate();
    }

    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
      e.Cancel = true;
      Hide();
    }
  }
}<file_sep>/WFEditSpatial/Forms/FormPrepareDune.cs
﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using EditSpatial.Model;
using LibEditSpatial.Dialogs;
using LibEditSpatial.Model;

namespace EditSpatial.Forms
{
  public partial class FormPrepareDune : Form
  {
    private bool closing;
    private Process process;

    public FormPrepareDune()
    {
      InitializeComponent();
    }

    public string BuildDir
    {
      get { return Path.Combine(Target, "build"); }
    }

    public string Target
    {
      get
      {
        return Path.Combine(TargetDir, ModuleName);
      }
    }

    public string TargetDir
    {
      get { return txtTargetDir.Text; }
      set { txtTargetDir.Text = value; }
    }

    public string ModuleName
    {
      get { return txtName.Text; }
      set { txtName.Text = value; }
    }

    public EditSpatialSettings Settings { get; set; }
    public SpatialModel Model { get; set; }

    private void OnBrowseClicke(object sender, EventArgs e)
    {
      txtTargetDir.Text = LibEditSpatial.Util.GetDir(txtTargetDir.Text);
    }

    private void DoCreateHost()
    {
      Util.UnzipArchive(
        Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "host.zip"), Target);
      txtResult.Text = "Host created in: " + Target;
    }

    private void OnCreateHostClick(object sender, EventArgs e)
    {
      if (Directory.Exists(Target))
      {
        var result = MessageBox.Show(
          string.Format(
            "The target directory '{0}' already consists if you continue, do you want to delete the directory before continuing?",
            Target),
          "Target dir exists", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

        if (result != DialogResult.Yes)
          return;

        try
        {
          Directory.Delete(Target, true);
        }
        catch
        {
        }
      }
      DoCreateHost();
    }

    private async Task RunCMake(string buildDir)
    {
      if (process != null) return;

      var info = new ProcessStartInfo
      {
        FileName = Path.Combine(Settings.CygwinDir, "bash"),
        Arguments = string.Format(
          "-c \"cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX={0} {1}\"",
          LibEditSpatial.Util.ToCygwin(Settings.DuneDir),
          LibEditSpatial.Util.ToCygwin(Target)
          ),
        WorkingDirectory = buildDir,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        CreateNoWindow = true
      };

#pragma warning disable 4014
      await StartProcess(info);
#pragma warning restore 4014
    }

    private async Task RunMake(string buildDir)
    {
      if (process != null) return;

      var info = new ProcessStartInfo
      {
        FileName = Path.Combine(Settings.CygwinDir, "bash"),
        Arguments =
          "-c make",
        WorkingDirectory = buildDir,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        CreateNoWindow = true
      };


#pragma warning disable 4014
      await StartProcess(info);
#pragma warning restore 4014

      DlgRun.WriteBatchFiles(buildDir, Settings.CygwinDir);

    }

    private void ReEnableUI()
    {
      process = null;
    }

    private void OnAddString(string data)
    {
      if (closing) return;
      if (string.IsNullOrEmpty(data)) return;

      if (InvokeRequired)
      {
        Invoke(new VoidStringDelegate(OnAddString), data);
        return;
      }
      txtResult.AppendText(data.Replace("\n", Environment.NewLine) + Environment.NewLine);
    }

    public async Task StartProcess(ProcessStartInfo info)
    {
      if (!string.IsNullOrEmpty(Settings.CygwinDir))
        info.EnvironmentVariables["PATH"] = Settings.CygwinDir + ";" + info.EnvironmentVariables["PATH"];

      process = new Process {StartInfo = info, EnableRaisingEvents = true};
      process.Exited += (o, e1) => ReEnableUI();
      process.OutputDataReceived += (o, e2) => OnAddString(e2.Data);
      process.ErrorDataReceived += (o, e3) => OnAddString(e3.Data);
      process.Start();
      Thread.Sleep(100);
      process.BeginOutputReadLine();
      process.BeginErrorReadLine();

      await Task.Run(() => process.WaitForExit());
    }

    private void DoCompile()
    {
      // create build dir
      if (!Directory.Exists(BuildDir))
        Directory.CreateDirectory(BuildDir);
      // run cmake
      
      Task.Factory.StartNew(() => RunCMake(BuildDir)
        .ContinueWith(prevTask => RunMake(BuildDir)));
    }

    private void OnCompileModel(object sender, EventArgs e)
    {
      if (!Directory.Exists(Target))
      {
        MessageBox.Show("Please create hosting application first", "Hosting application not present.",
          MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
      txtResult.Text = "Compiling Model";
      DoCompile();
    }

    private void DoExport()
    {
      // export model into host/src
      Model.ExportToDune(Path.Combine(Path.Combine(Target, "src"), "host.cc"));
      txtResult.Text = "Dune model exported into: " + Path.Combine(Target, "src");
    }

    private void OnExportModel(object sender, EventArgs e)
    {
      if (!Directory.Exists(Target))
      {
        MessageBox.Show("Please create hosting application first", "Hosting application not present.",
          MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
      DoExport();
    }

    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
      closing = true;
    }

    private DuneConfig GetConfigFromDir(string directory)
    {
      if (!Directory.Exists(directory)) return null;
      var files = Directory.GetFiles(directory, "*.conf", SearchOption.TopDirectoryOnly);
      if (files.Length > 0)
      {
        var file = files.FirstOrDefault(f => f.Contains("temp.conf"));
        if (string.IsNullOrEmpty(file))
          file = files[0];
        return DuneConfig.FromFile(file);
      }
      return null;
    }

    private void OnRunClick(object sender, EventArgs e)
    {
      if (!Directory.Exists(Target))
      {
        MessageBox.Show("Please create hosting application first", "Hosting application not present.",
          MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
      if (!Directory.Exists(BuildDir))
      {
        MessageBox.Show("Please compile application first", "Application not compiled.",
          MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }

      var runDir = Path.Combine(BuildDir, "src");
      var config = GetConfigFromDir(runDir);
      if (config != null)
        using (var dlg = new DlgRun
        {
          CygwinDir = Settings.CygwinDir,
          ParaViewDir = Settings.ParaViewDir,
          Config = config,
          FileName = Directory.GetFiles(runDir, "*.exe", SearchOption.TopDirectoryOnly).FirstOrDefault()
        })
        {
          dlg.ShowDialog(this);
        }
    }

    private void OnEditConfig(object sender, EventArgs e)
    {
      if (!Directory.Exists(Target))
      {
        MessageBox.Show("Please create hosting application first", "Hosting application not present.",
          MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
      if (!Directory.Exists(Path.Combine(Target, "build")))
      {
        MessageBox.Show("Please compile application first", "Application not compiled.",
          MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
      var runDir = Path.Combine(BuildDir, "src");
      var config = GetConfigFromDir(runDir);
      if (config != null)
        using (var dlg = new WFDuneRunner.MainForm())
        {
          dlg.OpenFile(config.FileName);
          dlg.ShowDialog(this);
        }
    }

    private void cmdFolder_Click(object sender, EventArgs e)
    {
      try
      {
        Process.Start(Path.Combine(txtTargetDir.Text, txtName.Text));
      }
      catch
      {
      }
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlRules.cs
﻿using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlRules : BaseSpatialControl
  {
    public ControlRules()
    {
      InitializeComponent();
    }

    private SBMLDocument Current { get; set; }

    private static string RuleName(int typeCode)
    {
      if (typeCode == libsbml.SBML_ALGEBRAIC_RULE)
        return "AlgebraicRule";
      if (typeCode == libsbml.SBML_RATE_RULE)
        return "RateRule";
      return "AssignmentRule";
    }

    public void InitializeFrom(SBMLDocument document)
    {
      grid.Rows.Clear();
      Current = document;
      if (document == null || document.getModel() == null) return;

      var model = document.getModel();


      for (long i = 0; i < model.getNumRules(); ++i)
      {
        var current = model.getRule(i);
        grid.Rows.Add(current.getVariable(),
          current.isSetMath() ? libsbml.formulaToL3String(current.getMath()) : "",
          RuleName(current.getTypeCode()));
      }
    }

    private Rule CreateRule(libsbmlcs.Model model, string type)
    {
      if (type == "AlgebraicRule")
        return model.createAlgebraicRule();
      if (type == "RateRule")
        return model.createRateRule();
      return model.createAssignmentRule();
    }

    public override void SaveChanges()
    {
      if (Current == null) return;
      for (var i = 0; i < grid.Rows.Count; ++i)
      {
        var row = grid.Rows[i];
        var value = (string) row.Cells[1].Value;
        if (string.IsNullOrEmpty(value)) continue;
        var node = libsbml.parseL3Formula(value);
        if (node == null) continue;

        var current = Current.getModel().getRuleByVariable((string) row.Cells[0].Value);
        if (current == null)
        {
          current = CreateRule(Current.getModel(), (string) row.Cells[2].Value);
        }

        current.setVariable((string) row.Cells[0].Value);
        current.setMath(node);
      }

      OnModelChanged();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }
  }
}<file_sep>/LibEditSpatial/Model/NewtonConfig.cs
using System.Collections.Generic;

namespace LibEditSpatial.Model
{
  public class NewtonConfig
  {
    public int LinearVerbosity { get; set; }
    public double ReassembleThreshold { get; set; }
    public int LineSearchMaxIterations { get; set; }
    public int MaxIterations { get; set; }
    public double AbsoluteLimit { get; set; }
    public double Reduction { get; set; }
    public double LinearReduction { get; set; }
    public double LineSearchDampingFactor { get; set; }
    public int Verbosity { get; set; }

    public static NewtonConfig Default
    {
      get
      {
        return new NewtonConfig
        {
          LinearVerbosity = 0,
          ReassembleThreshold = 0,
          LineSearchMaxIterations = 5,
          MaxIterations = 30,
          AbsoluteLimit = 1e-18,
          Reduction = 1e-8,
          LinearReduction = 1e-4,
          LineSearchDampingFactor = 0.5,
          Verbosity = 0
        };
      }
    }

    public static NewtonConfig FromDict(Dictionary<string, string> dict)
    {
      var result = new NewtonConfig
      {
        LinearVerbosity = dict.Get<int>("LinearVerbosity"),
        ReassembleThreshold = dict.Get<double>("ReassembleThreshold"),
        LineSearchMaxIterations = dict.Get<int>("LineSearchMaxIterations"),
        MaxIterations = dict.Get<int>("MaxIterations"),
        AbsoluteLimit = dict.Get<double>("AbsoluteLimit"),
        Reduction = dict.Get<double>("Reduction"),
        LinearReduction = dict.Get<double>("LinearReduction"),
        LineSearchDampingFactor = dict.Get<double>("LineSearchDampingFactor"),
        Verbosity = dict.Get<int>("Verbosity")
      };
      return result;
    }

    public Dictionary<string, string> ToDict()
    {
      var result = new Dictionary<string, string>();
      result["LinearVerbosity"] = LinearVerbosity.ToString();
      result["ReassembleThreshold"] = ReassembleThreshold.ToString();
      result["LineSearchMaxIterations"] = LineSearchMaxIterations.ToString();
      result["MaxIterations"] = MaxIterations.ToString();
      result["AbsoluteLimit"] = AbsoluteLimit.ToString();
      result["Reduction"] = Reduction.ToString();
      result["LinearReduction"] = LinearReduction.ToString();
      result["LineSearchDampingFactor"] = LineSearchDampingFactor.ToString();
      result["Verbosity"] = Verbosity.ToString();
      return result;
    }
  }
}<file_sep>/WFEditSpatial/Templates/local_operator.hh
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set ts=4 sw=2 et sts=2:
#ifndef DUNE_PDELAB_MULTICOMPONENTTRANSPORTOP_HH
#define DUNE_PDELAB_MULTICOMPONENTTRANSPORTOP_HH

#include<dune/common/exceptions.hh>
#include<dune/common/fvector.hh>
#include<dune/common/static_assert.hh>

#include <dune/geometry/referenceelements.hh>

#include<dune/pdelab/common/geometrywrapper.hh>
#include<dune/pdelab/common/function.hh>
#include<dune/pdelab/localoperator/defaultimp.hh>
#include<dune/pdelab/localoperator/pattern.hh>
#include<dune/pdelab/localoperator/flags.hh>
#include<dune/pdelab/localoperator/idefault.hh>

#define ANALYTICAL_JACOBIAN FALSE

namespace Dune {
  namespace PDELab {


    // reaction base class
    // do not compute anything
    class ReactionBaseAdapter
    {
    public:
      //! constructor stores reference to the model
      ReactionBaseAdapter()
      {}

      //! do one step
      template<typename EG, typename LFSU, typename X, typename LFSV, typename R, typename DomainType>
      void evaluate(const EG& eg, const LFSU& lfsu, const X& x, const LFSV& lfsv, R& r, const DomainType& x_)
      {

      }

      template<typename RF>
      void preStep(RF time, RF dt, int stages)
      {

      }

    };

    //! traits class for two phase parameter class
    template<typename GV, typename RF>
    struct DiffusionParameterTraits
    {
      //! \brief the grid view
      typedef GV GridViewType;

      //! \brief Enum for domain dimension
      enum {
        //! \brief dimension of the domain
        dimDomain = GV::dimension
      };

      //! \brief Export type for domain field
      typedef typename GV::Grid::ctype DomainFieldType;

      //! \brief domain type
      typedef Dune::FieldVector<DomainFieldType, dimDomain> DomainType;

      //! \brief domain type
      typedef Dune::FieldVector<DomainFieldType, dimDomain - 1> IntersectionDomainType;

      //! \brief Export type for range field
      typedef RF RangeFieldType;

      //! \brief range type
      typedef Dune::FieldVector<RF, GV::dimensionworld> RangeType;

      //! grid types
      typedef typename GV::Traits::template Codim<0>::Entity ElementType;
      typedef typename GV::Intersection IntersectionType;


      /** \brief Class to define the boundary condition types
      */
      struct ConvectionDiffusionBoundaryConditions
      {
        enum Type { Dirichlet = 1, Neumann = -1, Outflow = -2, None = -3 }; // BC requiring constraints must be >0 if
        // constraints assembler coming with PDELab is used
      };

      typedef typename ConvectionDiffusionBoundaryConditions::Type BCType;
    };


    //! base class for parameter class
    template<class T, class Imp>
    class DiffusionMulticomponentInterface
    {
    public:
      typedef T Traits;
      typedef typename Traits::BCType BCType;

      //! scalar diffusion coefficient
      typename Traits::RangeFieldType
        D(const typename Traits::ElementType& e, const typename Traits::DomainType& x, std::size_t i) const
      {
        return asImp().component(i).D(e, x);
      }

      //! source term
      typename Traits::RangeFieldType
        q(const typename Traits::ElementType& e, const typename Traits::DomainType& x, std::size_t i) const
      {
        return asImp().component(i).q(e, x);
      }

      BCType
        bctype(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x, std::size_t i) const
      {
        return asImp().component(i).bctype(is, x);
      }

      //! Dirichlet boundary condition on inflow
      typename Traits::RangeFieldType
        g(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x, std::size_t i) const
      {
        return asImp().component(i).g(is, x);
      }

      //! Neumann boundary condition
      typename Traits::RangeFieldType
        j(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x, std::size_t i) const
      {
        return asImp().component(i).j(is, x);
      }

      template<std::size_t i>
      void setTime(typename Traits::RangeFieldType t)
      {
        asImp().setTime(t);
      }

    private:
      Imp& asImp() { return static_cast<Imp &> (*this); }
      const Imp& asImp() const { return static_cast<const Imp &>(*this); }
    };




    /** a local operator for a cell-centered finite folume scheme for
    the diffusion-reaction equation

    \nabla \cdot \{v u- D \nabla u \} = q in \Omega
    u = g on \Gamma_D
    \{v u - D \nabla u \} \cdot \nu = j on \Gamma_N
    outflow on \Gamma_O

    Modified version for the case

    d_t (c(x,t)u(x,t)) + \nabla \cdot \{v u - D \nabla u \} = q in \Omega

    where c(x,t) may become zero. We assume that the following holds:

    c(x,t+dt) <= eps  ==>  c(x,t) <= eps



    \tparam TP  parameter class implementing ComponentDiffusionParameterInterface
    */
    template<typename TP, typename RA = ReactionBaseAdapter>
    class MulticomponentCCFVSpatialDiffusionOperator :
      public NumericalJacobianVolume<MulticomponentCCFVSpatialDiffusionOperator<TP, RA> >,
      public NumericalJacobianApplyVolume<MulticomponentCCFVSpatialDiffusionOperator<TP, RA> >,
      public NumericalJacobianSkeleton<MulticomponentCCFVSpatialDiffusionOperator<TP, RA> >,
      public NumericalJacobianBoundary<MulticomponentCCFVSpatialDiffusionOperator<TP, RA> >,
      public NumericalJacobianApplySkeleton<MulticomponentCCFVSpatialDiffusionOperator<TP, RA> >,
      public NumericalJacobianApplyBoundary<MulticomponentCCFVSpatialDiffusionOperator<TP, RA> >,

      public FullSkeletonPattern,
      public FullVolumePattern,
      public LocalOperatorDefaultFlags,
      public InstationaryLocalOperatorDefaultMethods<typename TP::Traits::RangeFieldType>
    {

    public:
      // pattern assembly flags
      enum { doPatternVolume = true };
      enum { doPatternSkeleton = true };

      // residual assembly flags
      enum { doAlphaVolume = true };
      enum { doAlphaSkeleton = true };
      enum { doAlphaBoundary = true };

      enum { dim = TP::Traits::GridViewType::dimension };

      typedef typename TP::BCType BCType;

      MulticomponentCCFVSpatialDiffusionOperator(TP& tp_)
        : tp(tp_), ra(raDefault())
      {
      }

      MulticomponentCCFVSpatialDiffusionOperator(TP& tp_, RA& ra_)
        : tp(tp_), ra(ra_)
      {
      }


      // volume integral depending on test and ansatz functions
      template<typename EG, typename LFSU, typename X, typename LFSV, typename R>
      void alpha_volume(const EG& eg, const LFSU& lfsu, const X& x, const LFSV& lfsv, R& r) const
      {

        typedef typename LFSV::template Child<0>::Type Space;

        // domain and range field type
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::DomainFieldType DF;

        // dimensions
        const int dim = EG::Geometry::dimension;

        // cell center
        const Dune::FieldVector<DF, dim>&
          inside_local = Dune::ReferenceElements<DF, dim>::general(eg.entity().type()).position(0, 0);


        // here could described the source (right hand side of the sytem of equations)
        // for each equation (big ammount of code)
        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {
          // evaluate source term
          typename TP::Traits::RangeFieldType q = tp.q(eg.entity(), inside_local, k);
          r.accumulate(lfsv, k, -q*eg.geometry().volume());
        }

        ra.evaluate(eg.entity(), lfsu, x, lfsv, r, inside_local);

      }



      // skeleton integral depending on test and ansatz functions
      // We put the Dirchlet evaluation also in the alpha term to save some geometry evaluations
      template<typename IG, typename LFSU, typename X, typename LFSV, typename R>
      void alpha_boundary(const IG& ig,
        const LFSU& lfsu_s, const X& x_s, const LFSV& lfsv_s,
        R& r_s) const
      {
        typedef typename LFSV::template Child<0>::Type Space;

        // domain and range field type
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::DomainFieldType DF;
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::RangeFieldType RF;

        // face geometry
        const Dune::FieldVector<DF, IG::dimension - 1>&
          face_local = Dune::ReferenceElements<DF, IG::dimension - 1>::general(ig.geometry().type()).position(0, 0);
        RF face_volume = ig.geometry().volume();

        const Dune::FieldVector<DF, IG::dimension>&
          inside_local = Dune::ReferenceElements<DF, IG::dimension>::general(ig.inside()->type()).position(0, 0);



        Dune::FieldVector<DF, IG::dimension>
          inside_global = ig.inside()->geometry().center();
        Dune::FieldVector<DF, IG::dimension>
          outside_global = ig.geometry().center();
        inside_global -= outside_global;
        RF distance = inside_global.two_norm();

        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {
          typename TP::Traits::RangeFieldType D_inside = tp.D(*(ig.inside()), inside_local, k);
          // evaluate boundary condition type
          BCType bc = tp.bctype(ig.intersection(), face_local, k);

          // do things depending on boundary condition type
          if (bc == BCType::Neumann) // Neumann boundary
          {
            typename TP::Traits::RangeFieldType j = tp.j(ig.intersection(), face_local, k);
            r_s.accumulate(lfsu_s, k, j*face_volume);
          }


          if (bc == BCType::Dirichlet) // Dirichlet boundary
          {
            typename TP::Traits::RangeFieldType g;
            g = tp.g(ig.intersection(), face_local, k);
            r_s.accumulate(lfsu_s, k, (-D_inside*(g - x_s(lfsu_s, k)) / distance)*face_volume);
          }
        }

      }

#ifdef ANALYTICAL_JACOBIAN
      // jacobian of boundary term
      template<typename IG, typename LFSU, typename X, typename LFSV, typename M>
      void jacobian_boundary(const IG& ig,
        const LFSU& lfsu_s, const X& x_s, const LFSV& lfsv_s,
        M& mat_ss) const
      {
        typedef typename LFSV::template Child<0>::Type Space;

        // domain and range field type
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::DomainFieldType DF;
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::RangeFieldType RF;

        // face geometry
        const Dune::FieldVector<DF, IG::dimension - 1>&
          face_local = Dune::ReferenceElements<DF, IG::dimension - 1>::general(ig.geometry().type()).position(0, 0);
        RF face_volume = ig.geometry().volume();

        const Dune::FieldVector<DF, IG::dimension>&
          inside_local = Dune::ReferenceElements<DF, IG::dimension>::general(ig.inside()->type()).position(0, 0);
        Dune::FieldVector<DF, IG::dimension>
          inside_global = ig.inside()->geometry().center();
        Dune::FieldVector<DF, IG::dimension>
          outside_global = ig.geometry().center();
        inside_global -= outside_global;
        RF distance = inside_global.two_norm();

        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {
          // evaluate boundary condition type
          int bc = tp.bctype(ig.intersection(), face_local, k);

          // do things depending on boundary condition type
          if (bc == BCType::Neumann) // Neumann boundary
          {
            return;
          }

          if (bc == BCType::Dirichlet) // Dirichlet boundary
          {
            typename TP::Traits::RangeFieldType D_inside = tp.D(*(ig.inside()), inside_local, k);
            mat_ss.accumulate(lfsu_s, k, lfsu_s, k, D_inside / distance*face_volume);
            return;
          }
        }
      }
#endif //ANALYTICAL_JACOBIAN


      // skeleton integral depending on test and ansatz functions
      // We put the Dirchlet evaluation also in the alpha term to save some geometry evaluations
      template<typename IG, typename LFSU, typename X, typename LFSV, typename R>
      void alpha_skeleton(const IG& ig,
        const LFSU& lfsu_s, const X& x_s, const LFSV& lfsv_s,
        const LFSU& lfsu_n, const X& x_n, const LFSV& lfsv_n,
        R& r_s, R& r_n) const
      {

        typedef typename LFSV::template Child<0>::Type Space;

        // domain and range field type
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::DomainFieldType DF;
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::RangeFieldType RF;

        // face geometry
        RF face_volume = ig.geometry().volume();
        const Dune::FieldVector<DF, IG::dimension>&
          inside_local = Dune::ReferenceElements<DF, IG::dimension>::general(ig.inside()->type()).position(0, 0);
        const Dune::FieldVector<DF, IG::dimension>&
          outside_local = Dune::ReferenceElements<DF, IG::dimension>::general(ig.outside()->type()).position(0, 0);

        // distance between cell centers in global coordinates
        Dune::FieldVector<DF, IG::dimension>
          inside_global = ig.inside()->geometry().center();
        Dune::FieldVector<DF, IG::dimension>
          outside_global = ig.outside()->geometry().center();
        inside_global -= outside_global;
        RF distance = inside_global.two_norm();

        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {
          // evaluate diffusion coefficients
          typename TP::Traits::RangeFieldType D_inside = tp.D(*(ig.inside()), inside_local, k);
          typename TP::Traits::RangeFieldType D_outside = tp.D(*(ig.outside()), outside_local, k);
          typename TP::Traits::RangeFieldType D_avg = 2.0 / (1.0 / (D_inside + 1E-40) + 1.0 / (D_outside + 1E-40));

          // diffusive flux
          r_s.accumulate(lfsu_s, k, -(D_avg*(x_n(lfsu_n, k) - x_s(lfsu_s, k)) / distance)*face_volume);
          r_n.accumulate(lfsu_n, k, (D_avg*(x_n(lfsu_n, k) - x_s(lfsu_s, k)) / distance)*face_volume);
        }

      }

#ifdef ANALYTICAL_JACOBIAN
      // jacobian of skeleton term
      template<typename IG, typename LFSU, typename X, typename LFSV, typename M>
      void jacobian_skeleton(const IG& ig,
        const LFSU& lfsu_s, const X& x_s, const LFSV& lfsv_s,
        const LFSU& lfsu_n, const X& x_n, const LFSV& lfsv_n,
        M& mat_ss, M& mat_sn,
        M& mat_ns, M& mat_nn) const
      {
        typedef typename LFSV::template Child<0>::Type Space;

        // domain and range field type
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::DomainFieldType DF;
        typedef typename Space::Traits::FiniteElementType::
          Traits::LocalBasisType::Traits::RangeFieldType RF;

        // face geometry
        RF face_volume = ig.geometry().volume();
        const Dune::FieldVector<DF, IG::dimension>&
          inside_local = Dune::ReferenceElements<DF, IG::dimension>::general(ig.inside()->type()).position(0, 0);
        const Dune::FieldVector<DF, IG::dimension>&
          outside_local = Dune::ReferenceElements<DF, IG::dimension>::general(ig.outside()->type()).position(0, 0);


        // distance between cell centers in global coordinates
        Dune::FieldVector<DF, IG::dimension>
          inside_global = ig.inside()->geometry().center();
        Dune::FieldVector<DF, IG::dimension>
          outside_global = ig.outside()->geometry().center();
        inside_global -= outside_global;
        RF distance = inside_global.two_norm();

        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {

          // evaluate diffusion coefficients
          typename TP::Traits::RangeFieldType D_inside = tp.D(*(ig.inside()), inside_local, k);
          typename TP::Traits::RangeFieldType D_outside = tp.D(*(ig.outside()), outside_local, k);
          typename TP::Traits::RangeFieldType D_avg = 2.0 / (1.0 / (D_inside + 1E-40) + 1.0 / (D_outside + 1E-40));

          // diffusive flux
          mat_ss.accumulate(lfsu_s, k, lfsu_s, k, D_avg / distance*face_volume);
          mat_sn.accumulate(lfsu_n, k, lfsu_n, k, -D_avg / distance*face_volume);

          mat_ns.accumulate(lfsu_s, k, lfsu_s, k, -D_avg / distance*face_volume);
          mat_nn.accumulate(lfsu_n, k, lfsu_n, k, D_avg / distance*face_volume);

        }

      }
#endif //ANALYTICAL_JACOBIAN

      //! to be called once before each time step
      void preStep(typename TP::Traits::RangeFieldType time, typename TP::Traits::RangeFieldType dt,
        int stages)
      {
        tp.preStep(time, dt, stages);
        ra.preStep(time, dt, stages);
      }

      //! to be called once before each stage
      void preStage(typename TP::Traits::RangeFieldType time, int r)
      {

      }

      //! to be called once at the end of each stage
      void postStage()
      {
      }

    private:
      static RA & raDefault()
      {
        static RA ra;
        return ra;
      }


      TP& tp;
      RA& ra;
    };


    /** a local operator for the storage operator
    *
    * \f{align*}{
    \int_\Omega c(x,t) uv dx
    * \f}
    *
    * version where c(x,t) may become zero.
    */
    template<class TP>
    class MulticomponentCCFVTemporalOperator
      : public NumericalJacobianVolume<MulticomponentCCFVTemporalOperator<TP> >,
      public NumericalJacobianApplyVolume<MulticomponentCCFVTemporalOperator<TP> >,
      public FullVolumePattern,
      public LocalOperatorDefaultFlags,
      public InstationaryLocalOperatorDefaultMethods<typename TP::Traits::RangeFieldType>
    {
    public:
      // pattern assembly flags
      enum { doPatternVolume = true };

      // residual assembly flags
      enum { doAlphaVolume = true };

      MulticomponentCCFVTemporalOperator(TP& tp_)
        : tp(tp_)
      {
      }

      // volume integral depending on test and ansatz functions
      template<typename EG, typename LFSU, typename X, typename LFSV, typename R>
      void alpha_volume(const EG& eg, const LFSU& lfsu, const X& x, const LFSV& lfsv, R& r) const
      {
        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {
          // residual contribution
          r.accumulate(lfsu, k, x(lfsu, k)*eg.geometry().volume());
        }
      }


#ifdef ANALYTICAL_JACOBIAN
      // jacobian of volume term
      template<typename EG, typename LFSU, typename X, typename LFSV, typename M>
      void jacobian_volume(const EG& eg, const LFSU& lfsu, const X& x, const LFSV& lfsv,
        M& mat) const
      {
        for (std::size_t k = 0; k<TP::COMPONENTS; k++)
        {
          // residual contribution
          mat.accumulate(lfsu, k, lfsu, k, eg.geometry().volume());
        }
      }
#endif //ANALYTICAL_JACOBIAN

      //! to be called once before each time step
      void preStep(typename TP::Traits::RangeFieldType time, typename TP::Traits::RangeFieldType dt,
        int stages)
      {
        tp.preStep(time, dt, stages);
        tp.setTimeTarget(time, dt);
      }

      //! to be called once before each stage
      void preStage(typename TP::Traits::RangeFieldType time, int r)
      {
      }

      //! to be called once at the end of each stage
      void postStage()
      {
      }


      //suggest time step, asked after first stage
      typename TP::Traits::RangeFieldType suggestTimestep(typename TP::Traits::RangeFieldType dt) const
      {
        return std::numeric_limits<typename TP::Traits::RangeFieldType>::max(); //initial value should be big enough
      }

    private:
      TP& tp;
      typename TP::Traits::RangeFieldType time;
    };

  }
}

#endif
<file_sep>/LibEditSpatial/Dialogs/FormSettings.cs
﻿using System;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Dialogs
{
  public partial class FormSettings : Form
  {
    private EditSpatialSettings _Settings;

    public FormSettings()
    {
      InitializeComponent();
    }

    public EditSpatialSettings Settings
    {
      get { return _Settings; }
      set
      {
        _Settings = value;
        UpdateUI();
      }
    }

    private void UpdateUI()
    {
      if (_Settings == null) return;

      txtCygwin.Text = _Settings.CygwinDir;
      txtParaviewDir.Text = _Settings.ParaViewDir;
      txtDuneDir.Text = _Settings.DuneDir;
      txtDefaultDir.Text = _Settings.DefaultDir;
      chkIgnoreCompartments.Checked = _Settings.IgnoreMultiCompartments;
    }

    private void OnBrowseCygwin(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.CygwinDir = Util.GetDir(_Settings.CygwinDir);
      txtCygwin.Text = _Settings.CygwinDir;
    }

    private void OnBrowsePV(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.ParaViewDir = Util.GetDir(_Settings.ParaViewDir);
      txtParaviewDir.Text = _Settings.ParaViewDir;
    }

    private void OnBrowseDune(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.DuneDir = Util.GetDir(_Settings.DuneDir);
      txtDuneDir.Text = _Settings.DuneDir;
    }

    private void txtDuneDir_TextChanged(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.DuneDir = txtDuneDir.Text;
    }

    private void txtParaviewDir_TextChanged(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.ParaViewDir = txtParaviewDir.Text;
    }

    private void txtCygwin_TextChanged(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.CygwinDir = txtCygwin.Text;
    }

    private void chkIgnoreCompartments_CheckedChanged(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.IgnoreMultiCompartments = chkIgnoreCompartments.Checked;
    }

    private void txtDefaultDir_TextChanged(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.DefaultDir = txtDefaultDir.Text;
    }

    private void cmdBrowseDefaultDir_Click(object sender, EventArgs e)
    {
      if (_Settings == null) return;
      _Settings.DefaultDir = Util.GetDir(_Settings.DefaultDir);
      txtDefaultDir.Text = _Settings.DefaultDir;
    }
  }
}<file_sep>/LibEditSpatial/Model/DmpPalette.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;

namespace LibEditSpatial.Model
{
  public class DmpPalette
  {
    private static DmpPalette _Default;

    public DmpPalette()
    {
      Colors = new List<Color>();
    }

    public static DmpPalette Default
    {
      get
      {
        if (_Default == null)
          _Default = new DmpPalette
          {
            IsFirstTransparent = true,
            Colors = new List<Color>
            {
              Color.Transparent
              ,
              Color.Red
              ,
              Color.Blue
              ,
              Color.Green
              ,
              Color.Yellow
              ,
              Color.Tomato
              ,
              Color.DarkSalmon
              ,
              Color.YellowGreen
              ,
              Color.DarkTurquoise
              ,
              Color.Khaki
              ,
              Color.MediumSeaGreen
              ,
              Color.Purple
              ,
              Color.Black
            }
          };
        return _Default;
      }
      set { _Default = value; }
    }

    public List<Color> Colors { get; set; }
    public bool IsFirstTransparent { get; set; }

    public Color GetColor(double val)
    {
      var index = Math.Max(Math.Min((int) Math.Round(val*Colors.Count), Colors.Count - 1), 0);
      return Colors[index];
    }

    public Bitmap ToImage(Size? size = null)
    {
      if (size == null)
        size = new Size {Width = 100, Height = 30};

      var image = new Bitmap(size.Value.Width, size.Value.Height);
      using (var graphics = Graphics.FromImage(image))
      {
        var length = size.Value.Width/(float) Colors.Count;
        for (var i = 0; i < Colors.Count; ++i)
        {
          graphics.FillRectangle(new SolidBrush(Colors[i]), length*i, 0f, length, size.Value.Height);
        }
      }
      return image;
    }

    public void SaveAs(string filename)
    {
      var builder = new StringBuilder();
      foreach (var color in Colors)
      {
        builder.AppendLine(ToArgbString(color));
      }
      File.WriteAllText(filename, builder.ToString());
    }

    public static string ToArgbString(Color oColor)
    {
      return ByteToHex(oColor.A) +
             ByteToHex(oColor.R) +
             ByteToHex(oColor.G) +
             ByteToHex(oColor.B);
    }

    public static string ByteToHex(byte oByte)
    {
      if (oByte == 0)
        return "00";
      var sResult = Convert.ToString(oByte, 16);
      if (sResult.Length == 1)
        sResult = "0" + sResult;
      return sResult;
    }

    public static Color ParseARGB(String argbString)
    {
      var oResult = Color.Empty;
      if (argbString == String.Empty) return Color.Empty;

      try
      {
        if (argbString.Length == 8)
        {
          int nAlpha = Convert.ToInt16(argbString.Substring(0, 2), 16);
          int nRed = Convert.ToInt16(argbString.Substring(2, 2), 16);
          int nGreen = Convert.ToInt16(argbString.Substring(4, 2), 16);
          int nBlue = Convert.ToInt16(argbString.Substring(6, 2), 16);
          oResult = Color.FromArgb(nAlpha, nRed, nGreen, nBlue);
        }
        else if (argbString.Length == 6)
        {
          int nRed = Convert.ToInt16(argbString.Substring(0, 2), 16);
          int nGreen = Convert.ToInt16(argbString.Substring(2, 2), 16);
          int nBlue = Convert.ToInt16(argbString.Substring(4, 2), 16);
          oResult = Color.FromArgb(255, nRed, nGreen, nBlue);
        }
        else
        {
          throw new Exception("The Color string has to be in ARGB");
        }
      }
      catch (Exception)
      {
      }
      return oResult;
    }

    public static DmpPalette FromFile(string fileName)
    {
      var lines = File.ReadAllLines(fileName);
      var result = new DmpPalette();
      foreach (var line in lines)
      {
        try
        {
          var color = ParseARGB(line.Replace("#", ""));
          result.Colors.Add(color);
        }
        catch
        {
        }
      }
      return result;
    }

    public int GetIndex(Color color)
    {
      return Colors.IndexOf(color);
    }
  }
}<file_sep>/WFEditSpatial/Forms/FormInitSpatial.cs
﻿using System;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Forms
{
  public partial class FormInitSpatial : Form
  {
    private AnalyticGeometry _analyticGeometry;
    private Geometry _geometry;
    private libsbmlcs.Model _model;
    private SampledFieldGeometry _sampleGeometry;

    public FormInitSpatial()
    {
      InitializeComponent();
      _model = null;
    }

    public CreateModel CreateModel
    {
      get
      {
        var result = new CreateModel
        {
          Geometry = GeometryModel
        };
        foreach (var item in lstSpatialSpecies.Items)
          result.Species.Add(item as SpatialSpecies);
        return result;
      }
    }

    public GeometrySettings GeometryModel
    {
      get
      {
        var geom = new GeometrySettings
        {
          Xmax = Util.SaveDouble(txtDimX.Text, 50),
          Ymax = Util.SaveDouble(txtDimY.Text, 50),
          Type = GeometryType.Default
        };

        if (radAnalytic.Checked)
        {
          geom.Type = GeometryType.Analytic;

          if (_analyticGeometry != null && _analyticGeometry.getNumAnalyticVolumes() > 0)
          {
            _analyticGeometry.ExpandMath();

            for (var i = 0; i < _analyticGeometry.getNumAnalyticVolumes(); ++i)
            {
              var current = _analyticGeometry.getAnalyticVolume(i);
              var math = libsbml.formulaToL3String(current.getMath());
              if (math.Contains(" width "))
                geom.UsedSymbols.Add("width");
              if (math.Contains(" height "))
                geom.UsedSymbols.Add("height");
              if (math.Contains(" Xmax "))
                geom.UsedSymbols.Add("Xmax");
              if (math.Contains(" Ymax "))
                geom.UsedSymbols.Add("Ymax");
              if (math.Contains(" Xmin "))
                geom.UsedSymbols.Add("Xmin");
              if (math.Contains(" Ymin "))
                geom.UsedSymbols.Add("Ymin");
            }
          }
        }

        if (radSample.Checked)
        {
          geom.Type = GeometryType.Sample;
        }


        return
          geom;
      }
    }

    public SpatialModel SpatialModel { get; set; }

    private void OnCancelClick(object sender, EventArgs e)
    {
      Close();
    }

    private void OnFinishClick(object sender, EventArgs e)
    {
      if (tabControl1.SelectedIndex == 1)
      {
        txtDimX.Text = txtWidth.Text;
        txtDimY.Text = txtHeight.Text;
      }

      if (radAnalytic.Checked)
      {
        controlAnalyticGeometry1.SaveChanges();
      }

      if (radSample.Checked)
      {
        controlSampleFieldGeometry1.SaveChanges();
      }

      Close();
    }

    private void OnAllSpeciesDoubleClick(object sender, EventArgs e)
    {
      AddSelected();
    }

    private void OnSpatialSpeciesDoubleClick(object sender, EventArgs e)
    {
      RemoveSelected();
    }

    private void AddSelected()
    {
      var selected = lstAllSpecies.SelectedItem;
      if (selected == null) return;
      AddSelected(selected as string);
    }

    private void AddSelected(Species species)
    {
      if (species == null || _model == null) return;


      var diffusionX = species.getDiffusionX();
      var diffusionY = species.getDiffusionY();
      var Xmax = species.getXMaxBC();
      var Xmin = species.getXMinBC();
      var Ymax = species.getYMaxBC();
      var Ymin = species.getYMinBC();
      var bcType = species.getBcType();


      // assignments do not need to diffuse by themselves automatically
      var defaultDiff =
        species.getModel().
          getAssignmentRuleByVariable(species.getId()) == null
          ? 0.001
          : 0.0;

      var spatialSpecies = new SpatialSpecies
      {
        Id = species.getId(),
        DiffusionX = diffusionX.HasValue
          ? diffusionX.Value
          : diffusionY.HasValue ? diffusionY.Value : defaultDiff,
        DiffusionY = diffusionY.HasValue
          ? diffusionY.Value
          : diffusionX.HasValue ? diffusionX.Value : defaultDiff,
        MinBoundaryX = Xmin.HasValue ? Xmin.Value : 0,
        MaxBoundaryX = Xmax.HasValue ? Xmax.Value : 0,
        MinBoundaryY = Ymin.HasValue ? Ymin.Value : 0,
        MaxBoundaryY = Ymax.HasValue ? Ymax.Value : 0,
        BCType = bcType,
        InitialCondition = species.getInitialExpession()
      };

      lstSpatialSpecies.Items.Add(spatialSpecies);

      grid.Rows.Add(spatialSpecies.Id,
        spatialSpecies.DiffusionX.ToString(CultureInfo.InvariantCulture),
        spatialSpecies.DiffusionY.ToString(CultureInfo.InvariantCulture),
        spatialSpecies.InitialCondition,
        spatialSpecies.MaxBoundaryX.ToString(CultureInfo.InvariantCulture), 
        spatialSpecies.MaxBoundaryY.ToString(CultureInfo.InvariantCulture),
        spatialSpecies.MinBoundaryX.ToString(CultureInfo.InvariantCulture), 
        spatialSpecies.MinBoundaryY.ToString(CultureInfo.InvariantCulture),
        spatialSpecies.BCType);
    }

    private void AddSelected(string id)
    {
      if (id == null || _model == null) return;
      if (lstSpatialSpecies.Items.Contains(id)) return;

      AddSelected(_model.getSpecies(id));
    }

    private void RemoveSelected()
    {
      var selected = lstSpatialSpecies.SelectedItem;
      if (selected == null) return;
      RemoveSelected(selected as SpatialSpecies);
    }

    private void RemoveSelected(string id)
    {
      var species = GetSpecies(id);

      RemoveSelected(species);
    }

    private SpatialSpecies GetSpecies(string id)
    {
      SpatialSpecies species = null;
      for (var i = lstSpatialSpecies.Items.Count - 1; i >= 0; i--)
      {
        var current = lstSpatialSpecies.Items[i] as SpatialSpecies;
        if (current != null && current.Id == id)
        {
          species = current;
          break;
        }
      }
      return species;
    }

    private void RemoveSelected(SpatialSpecies o)
    {
      if (o == null) return;
      var current = o;
      lstSpatialSpecies.Items.Remove(o);

      for (var i = grid.Rows.Count - 1; i >= 0; i--)
      {
        if (current.Id == (string) grid.Rows[i].Cells[0].Value)
          grid.Rows.RemoveAt(i);
      }
    }

    private void OnFirstShown(object sender, EventArgs e)
    {
      UpdateUI();
    }

    private void UpdateUI()
    {
      if (SpatialModel == null ||
          SpatialModel.Document == null ||
          SpatialModel.Document.getModel() == null) return;

      _model = SpatialModel.Document.getModel();

      // Correct compartments & Transport      
      if (!MainForm.Settings.IgnoreMultiCompartments)
        SpatialModel.CorrectCompartmentsAndTransport(_model);

      txtDimX.Text = SpatialModel.Width.ToString();
      txtDimY.Text = SpatialModel.Height.ToString();

      lstSpatialSpecies.Items.Clear();
      grid.Rows.Clear();

      for (var i = 0; i < _model.getNumSpecies(); ++i)
      {
        var current = _model.getSpecies(i);
        if (current == null) continue;
        lstAllSpecies.Items.Add(current.getId());
        if (current.getConstant() || current.getBoundaryCondition())
          continue;

        AddSelected(current);
      }

      var geom = SpatialModel.Geometry;
      if (geom == null) return;

      _analyticGeometry = geom.GetFirstAnalyticGeometry();
      if (_analyticGeometry != null)
      {
        if (_analyticGeometry.getNumAnalyticVolumes() == 1 && _analyticGeometry.getAnalyticVolume(0).isSetMath()
            && (libsbml.formulaToL3String(_analyticGeometry.getAnalyticVolume(0).getMath()) == "1"))
          radDefault.Checked = true;
        else
          radAnalytic.Checked = true;
      }

      _sampleGeometry = geom.GetFirstSampledFieldGeometry();
      if (_sampleGeometry != null)
      {
        radSample.Checked = true;
      }
    }

    private void ShowPage(int index)
    {
      if (index < 0) index = 0;
      if (index >= tabControl1.TabCount) index = tabControl1.TabCount - 1;

      var oldIndex = tabControl1.SelectedIndex;
      if (oldIndex == index) return;

      tabControl1.SelectedIndex = index;
    }

    private void OnPrevClick(object sender, EventArgs e)
    {
      ShowPage(tabControl1.SelectedIndex - 1);
    }

    private void OnNextClick(object sender, EventArgs e)
    {
      ShowPage(tabControl1.SelectedIndex + 1);
    }

    private void OnCellEndEdit(object sender, DataGridViewCellEventArgs e)
    {
      var row = grid.Rows[e.RowIndex];
      var current = (string) row.Cells[0].Value;
      var species = GetSpecies(current);

      switch (e.ColumnIndex)
      {
        case 1:
        {
          species.DiffusionX = row.Cells[1].GetDouble();
          break;
        }
        case 2:
        {
          species.DiffusionY = row.Cells[2].GetDouble();
          break;
        }
        case 3:
        {
          var node = libsbml.parseL3Formula((string) row.Cells[3].Value);
          if (node != null)
          {
            var formula = libsbml.formulaToL3String(node);
            species.InitialCondition = formula;
            row.Cells[3].Value = formula;
            row.Cells[3].Style.BackColor = Color.LightGreen;
          }
          else
          {
            row.Cells[3].Style.BackColor = Color.Pink;
          }


          break;
        }
        case 4:
        {
          species.MaxBoundaryX = row.Cells[4].GetDouble();
          break;
        }
        case 5:
        {
          species.MaxBoundaryY = row.Cells[5].GetDouble();
          break;
        }
        case 6:
        {
          species.MinBoundaryX = row.Cells[6].GetDouble();
          break;
        }
        case 7:
        {
          species.MinBoundaryY = row.Cells[7].GetDouble();
          break;
        }
        case 8:
        {
          species.BCType = (string) row.Cells[8].Value;
          break;
        }
        default:
          break;
      }
    }

    private void OnUserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
    {
      e.Cancel = true;
      var id = (string) e.Row.Cells[0].Value;
      RemoveSelected(id);
    }

    private void OnSelectedTabChanged(object sender, EventArgs e)
    {
      if (tabControl1.SelectedIndex == 0)
      {
        txtDimX.Text = txtWidth.Text;
        txtDimY.Text = txtHeight.Text;
        cmdNext.Enabled = true;
        cmdPrev.Enabled = false;
      }
      else if (tabControl1.SelectedIndex == 1)
      {
        txtWidth.Text = txtDimX.Text;
        txtHeight.Text = txtDimY.Text;
        cmdNext.Enabled = false;
        cmdPrev.Enabled = true;
      }
    }

    private void radAnalytic_CheckedChanged(object sender, EventArgs e)
    {
      controlAnalyticGeometry1.Visible = true;
      controlSampleFieldGeometry1.Visible = false;

      if (!radAnalytic.Checked) return;

      _geometry = SpatialModel.Geometry;

      if (SpatialModel.Document == null)
        return;

      if (_geometry == null)
      {
        _geometry = new Geometry(3, 1);
      }

      SpatialModel.CreateCoordinateSystem(_geometry,
        SpatialModel.Document.getModel(),
        new GeometrySettings
        {
          Xmax = Util.SaveDouble(txtDimX.Text, 50),
          Ymax = Util.SaveDouble(txtDimY.Text, 50)
        }
        );

      _analyticGeometry = _geometry.GetFirstAnalyticGeometry();

      if (_analyticGeometry == null)
      {
        _analyticGeometry = _geometry.createAnalyticGeometry();
        var vol = _analyticGeometry.createAnalyticVolume();
        vol.setId("vol0");
        vol.setDomainType("");
        vol.setFunctionType(libsbml.SPATIAL_FUNCTIONKIND_LAYERED);
        vol.setOrdinal(0);
        vol.setMath(libsbml.parseFormula("1"));
      }

      if (!_analyticGeometry.isSetId())
        _analyticGeometry.setId("ana1");

      controlAnalyticGeometry1.InitializeFrom(_geometry, _analyticGeometry);
    }

    private void radSample_CheckedChanged(object sender, EventArgs e)
    {
      controlAnalyticGeometry1.Visible = false;
      controlSampleFieldGeometry1.Visible = true;

      if (!radSample.Checked) return;

      _geometry = SpatialModel.Geometry;

      if (SpatialModel.Document == null) return;

      if (_geometry == null)
      {
        _geometry = new Geometry(3, 1);
      }

      SpatialModel.CreateCoordinateSystem(_geometry,
        SpatialModel.Document.getModel(),
        new GeometrySettings
        {
          Xmax = Util.SaveDouble(txtDimX.Text, 50),
          Ymax = Util.SaveDouble(txtDimY.Text, 50)
        }
        );

      _sampleGeometry = _geometry.GetFirstSampledFieldGeometry();

      if (_sampleGeometry == null)
      {
        _sampleGeometry = _geometry.createSampledFieldGeometry();
      }

      if (!_sampleGeometry.isSetId())
        _sampleGeometry.setId("sample1");

      controlSampleFieldGeometry1.InitializeFrom(_geometry, _sampleGeometry.getId());
    }

    private void radDefault_CheckedChanged(object sender, EventArgs e)
    {
      controlAnalyticGeometry1.Visible = false;
      controlSampleFieldGeometry1.Visible = false;
    }

    private void OnApplyDiffClick(object sender, EventArgs e)
    {
      double defaultDiff;
      if (!double.TryParse(txtDiffDefault.Text, out defaultDiff))
        return;

      for (var i = 0; i < grid.Rows.Count; ++i)
      {
        var current = grid.Rows[i];
        if (current.IsNewRow) break;
        current.Cells[1].Value = defaultDiff;
        current.Cells[2].Value = defaultDiff;

        var id = current.Cells[0].Value as string;
        var species = GetSpecies(id);
        if (species == null)
          continue;

        species.DiffusionX = defaultDiff;
        species.DiffusionY = defaultDiff;
      }
    }

    private void OnMakeIsotropic(object sender, EventArgs e)
    {
      for (var i = 0; i < grid.Rows.Count; ++i)
      {
        var current = grid.Rows[i];
        if (current.IsNewRow) break;
        current.Cells[2].Value = current.Cells[1].Value;

        var id = current.Cells[0].Value as string;
        var species = GetSpecies(id);
        if (species == null)
          continue;

        species.DiffusionY = species.DiffusionX;
      }
    }

    private void OnSortCompare(object sender, DataGridViewSortCompareEventArgs e)
    {
      if (e.CellValue1 == e.CellValue2)
      {
        e.SortResult = 0;
        return;
      }

      if (e.CellValue1 == null)
      {
        e.SortResult = -1;
        return;
      }

      if (e.CellValue2 == null)
      {
        e.SortResult = 1;
        return;
      }

      e.SortResult = ((IComparable)e.CellValue1).CompareTo(e.CellValue2);
    }
  }
}<file_sep>/WFDuneRunner/MainForm.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using libsbmlcs;
using LibEditSpatial.Dialogs;
using LibEditSpatial.Model;
using WFDuneRunner.Properties;

namespace WFDuneRunner
{
  public partial class MainForm : Form
  {
    private bool _isLoading;

    public MainForm()
    {
      InitializeComponent();

      ctrlDomain1.OpenDmpFile += (o, e) => OpenDmpFile(e);
      ctrlDomain1.BrowseDmpFile += (o, e) =>
      {
        string result;
        if (BrowseDmpFile(out result))
        {
          ctrlDomain1.Model.Geometry = result;
          ctrlDomain1.LoadModel(ctrlDomain1.Model);
        }
      };

      NewModel();
    }


    public DuneConfig Config { get; set; }
    public string FileName { get; set; }
    public string CurrentDir { get; set; }

    public string ExecutableFileName
    {
      get
      {
        var path = Path.GetDirectoryName(FileName);
        if (path == null) return null;
        var files = Directory.GetFiles(path, "*.exe");
        if (files.Length == 0) return null;
        return files[0];
      }
    }

    public void OpenFile(string filename)
    {
      FileName = filename;
      CurrentDir = Path.GetDirectoryName(filename);

      Config = DuneConfig.FromFile(filename);

      UpdateUI();
    }

    private string ConvertBCType(string bcString)
    {
      switch (bcString)
      {
        case "Dirichlet":
          return "1";
        default:
          //case "Neumann":
          return "-1";
        case "Outflow":
          return "-2";
        case "None":
          return "-3";
      }
    }

    private string GetBcType(string bcCode)
    {
      switch (bcCode)
      {
        case "1":
          return "Dirichlet";
        default:
          //case "-1":
          return "Neumann";
        case "-2":
          return "Outflow";
        case "-3":
          return "None";
      }
    }

    private void SetTitle()
    {
      if (string.IsNullOrWhiteSpace(FileName))
        Text = "Dune Runner";
      else
        Text = string.Format("Dune Runner [{0}]", Path.GetFileName(FileName));
    }

    private void UpdateParameters()
    {
      gridParameters.Rows.Clear();
      var parameters = Config["Reaction"];
      foreach (var item in parameters)
      {
        gridParameters.Rows.Add(item.Key, item.Value);
      }
    }

    private void UpdateVariables()
    {
      gridVariables.Rows.Clear();
      var data = Config["Data"];
      var varKeys = Config.GetVariableKeys();
      foreach (var item in varKeys)
      {
        var entries = Config[item];
        gridVariables.Rows.Add(item,
          entries["D"],
          entries.ContainsKey("BCType") ? GetBcType(entries["BCType"]) : "Neumann",
          entries.ContainsKey("Xmin") ? entries["Xmin"] : "0",
          entries.ContainsKey("Xmax") ? entries["Xmax"] : "0",
          entries.ContainsKey("Ymin") ? entries["Ymin"] : "0",
          entries.ContainsKey("Xmax") ? entries["Xmax"] : "0",
          (data != null && data.ContainsKey(item)) ? data[item] : "",
          null, null,
          entries.ContainsKey("file_compartment") ? entries["file_compartment"] : "0"
          );
      }
    }

    private void UpdateCompartments()
    {
      gridCompartments.Rows.Clear();
      foreach (var id in Config.CompartmentIds)
      {
        gridCompartments.Rows.Add(id, Config["Reaction"].Get("in_" + id, ""));
      }
    }


    internal void UpdateUI()
    {
      SetTitle();

      ctrlTime1.LoadModel(Config.TimeConfig);
      ctrlDomain1.LoadModel(Config.DomainConfig);
      ctrlNewton1.LoadModel(Config.NewtonConfig);
      ctrlGlobal1.LoadModel(Config.GlobalConfig);

      _isLoading = true;

      UpdateParameters();

      UpdateVariables();

      UpdateCompartments();


      cmdRun.Enabled = (!string.IsNullOrWhiteSpace(ExecutableFileName));

      _isLoading = false;
    }

    private void NewModel()
    {
      FileName = null;
      Config = new DuneConfig();
      UpdateUI();
    }

    private void OnNewFile(object sender, EventArgs e)
    {
      NewModel();
    }

    private void OnOpenFile(object sender, EventArgs e)
    {
      using (var dialog = new OpenFileDialog
      {
        Title = "Open Config",
        Filter = "Config files|*.conf|All files|*.*",
        InitialDirectory = CurrentDir
      })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
          OpenFile(dialog.FileName);
      }
    }

    private void OnSaveFile(object sender, EventArgs e)
    {
      // SaveAs for now
      // otherwise
      // SaveFile(FileName);
      OnSaveAs(sender, e);
    }

    private void OnAbout(object sender, EventArgs e)
    {
      using (var dlg = new AboutBox())
        dlg.ShowDialog();
    }

    private void SaveFile(string fileName)
    {
      Config.SaveAs(fileName);
      FileName = fileName;
      CurrentDir = Path.GetDirectoryName(fileName);
      UpdateUI();
    }

    private void OnSaveAs(object sender, EventArgs e)
    {
      using (var dialog = new SaveFileDialog
      {
        Title = "Save Config",
        Filter = "Config files|*.conf|All files|*.*",
        InitialDirectory = CurrentDir
      })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
          SaveFile(dialog.FileName);
      }
    }

    private void OnExit(object sender, EventArgs e)
    {
      Close();
    }

    private void OnRunClick(object sender, EventArgs e)
    {
      var fileName = ExecutableFileName;
      if (fileName == null) return;

      using (var dialog = new DlgRun
      {
        Config = Config,
        FileName = fileName, 
        CygwinDir = Settings.CygwinDir, 
        ParaViewDir = Settings.ParaViewDir
      })
      {
        dialog.ShowDialog(this);
      }
    }

    private void OnVariableChanged(object sender, DataGridViewCellEventArgs e)
    {
      if (_isLoading) return;

      if (e.RowIndex < 0) return;

      var row = gridVariables.Rows[e.RowIndex];
      if (row.IsNewRow) return;

      var id = row.Cells[0].Value as string;
      var entry = Config[id];
      var data = Config["Data"];

      switch (e.ColumnIndex)
      {
        case 1:
        {
          entry["D"] = row.Cells[1].Value as string;
          break;
        }
        case 2:
        {
          entry["BCType"] = ConvertBCType(row.Cells[2].Value as string);
          break;
        }
        case 3:
        {
          entry["Xmin"] = row.Cells[3].Value as string;
          break;
        }
        case 4:
        {
          entry["Xmax"] = row.Cells[4].Value as string;
          break;
        }
        case 5:
        {
          entry["Ymin"] = row.Cells[5].Value as string;
          break;
        }
        case 6:
        {
          entry["Ymax"] = row.Cells[6].Value as string;
          break;
        }
        case 7:
        {
          if (id != null)
          data[id] = row.Cells[7].Value as string;
          break;
        }
        default:
          break;
      }
    }

    private void OpenDmpFile(string file)
    {
      if (string.IsNullOrWhiteSpace(file)) return;

      if (FileName != null && !File.Exists(file))
      {
        file = Path.Combine(Path.GetDirectoryName(FileName), file);
        if (!File.Exists(file)) return;
      }
      try
      {
        using (var dlg = new WFEditDMP.MainForm())
        {
          dlg.OpenFile(file);
          dlg.StartPosition = FormStartPosition.CenterParent;
          dlg.ShowDialog(this);
        }
      }
      catch
      {
      }
    }

    private bool BrowseDmpFile(out string result)
    {
      result = String.Empty;
      var assign = false;
      using (var dlg = new OpenFileDialog
      {
        Title = "Open file",
        Filter = "DMP files|*.dmp|All files|*.*",
        InitialDirectory = CurrentDir
      })
      {
        if (dlg.ShowDialog() == DialogResult.OK)
        {
          var path = Path.GetDirectoryName(FileName);
          var filename = dlg.FileName;
          if (filename.StartsWith(path))
          {
            filename = filename.Replace(path, "");
            while (filename.StartsWith("\\"))
              filename = filename.Substring(1);
          }
          result = filename;
          assign = true;
        }
      }
      return assign;
    }

    private void OnVariableCellClick(object sender, DataGridViewCellEventArgs e)
    {
      if (_isLoading) return;

      if (e.RowIndex < 0) return;

      var row = gridVariables.Rows[e.RowIndex];
      if (row.IsNewRow) return;

      switch (e.ColumnIndex)
      {
        case 8: // open file
        {
          var file = row.Cells[7].Value as string;
          var id = row.Cells[0].Value as string;
          if (string.IsNullOrWhiteSpace(file))
          {
            var count = 1;
            var baseName = "ic_" + id;
            var name = baseName + ".dmp";

            var dir = Path.GetDirectoryName(FileName);
            while (File.Exists(Path.Combine(dir, name)))
            {
              name = string.Format("{0}_{1}.dmp", baseName, count++);
            }

            row.Cells[7].Value = name;
            var dmpFile = new DmpModel(Config.DomainConfig.GridX, Config.DomainConfig.GridY);
            dmpFile.MaxX = Config.DomainConfig.Width;
            dmpFile.MaxY = Config.DomainConfig.Height;
            file = Path.Combine(dir, name);
            dmpFile.SaveAs(file);
          }

          OpenDmpFile(file);
          break;
        }
        case 9: // browse file
        {
          string result;
          if (BrowseDmpFile(out result))
            row.Cells[7].Value = result;
          break;
        }
        default:
          break;
      }
    }

    private void OnCompartmentAssignmentChanged(object sender, DataGridViewCellEventArgs e)
    {
      if (_isLoading) return;

      if (e.RowIndex < 0) return;
      var row = gridCompartments.Rows[e.RowIndex];
      if (row.IsNewRow) return;

      switch (e.ColumnIndex)
      {
        case 1: // edited compartmentfile
        {
          var file = row.Cells[1].Value as string;
          var id = row.Cells[0].Value as string;
          Config.ApplyCompartmentMasking(id, file);
          UpdateVariables();
          break;
        }
        default:
          break;
      }
      
    }

    private void OnCompartmentCellClick(object sender, DataGridViewCellEventArgs e)
    {
      if (_isLoading) return;

      if (e.RowIndex < 0) return;

      var row = gridCompartments.Rows[e.RowIndex];
      if (row.IsNewRow) return;

      switch (e.ColumnIndex)
      {
        case 2: // browse file
        {
          string result;
          var id = row.Cells[0].Value as string;
          if (BrowseDmpFile(out result))
          {
            row.Cells[1].Value = result;
            Config.ApplyCompartmentMasking(id, result);
            UpdateVariables();
          }
          break;
        }
        case 3: // view compartment
        {
          var file = row.Cells[1].Value as string;
          var id = row.Cells[0].Value as string;
          if (string.IsNullOrWhiteSpace(file))
          {
            var count = 1;
            var baseName = "inside_" + id;
            var name = baseName + ".dmp";

            var dir = Path.GetDirectoryName(FileName);
            while (File.Exists(Path.Combine(dir, name)))
            {
              name = string.Format("{0}_{1}.dmp", baseName, count++);
            }

            row.Cells[1].Value = name;
            Config.ApplyCompartmentMasking(id, name);
            UpdateVariables();
            var dmpFile = new DmpModel(Config.DomainConfig.GridX, Config.DomainConfig.GridY) 
            { MaxX = Config.DomainConfig.Width, 
              MaxY = Config.DomainConfig.Height };
            file = Path.Combine(dir, name);
            dmpFile.SaveAs(file);
          }

          OpenDmpFile(file);
          break;
        }
        default:
          break;
      }
    }

    public void ClearExistingAssignments()
    {
      Config.ClearCompartmentMasking();
    }

    private void OnClearAllCompartmentAssignments(object sender, EventArgs e)
    {
      if (_isLoading) return;

      ClearExistingAssignments();
    }

    public void ApplyCompartmentAssignments(bool clearExisting = false)
    {

      if (clearExisting)
        ClearExistingAssignments();

      foreach (DataGridViewRow row in gridCompartments.Rows)
      {
        var compId = row.Cells[0].Value as string;
        var dmpFile = row.Cells[1].Value as string;

        Config.ApplyCompartmentMasking(compId, dmpFile);
        
      }
    }
    
    private void OnApplyCompartmentAssignments(object sender, EventArgs e)
    {
      if (_isLoading) return;

      ApplyCompartmentAssignments();

    }

    private void InitFromSBML(string fileName)
    {
      Config.InitFromSBML(fileName);

      gridCompartments.Rows.Clear();
      foreach (var id in Config.CompartmentIds)
      {
        gridCompartments.Rows.Add(id, Config["Reaction"].Get("in_" + id, ""));
      }
    }

    private void OnInitFromSBMLClick(object sender, EventArgs e)
    {
      using (var dlg = new OpenFileDialog
      {
        Title = "Open SBML file",
        Filter = "SBML files|*.xml;*.sbml|All files|*.*",
        InitialDirectory = CurrentDir
      })
      {
        if (dlg.ShowDialog() == DialogResult.OK)
        {
          InitFromSBML(dlg.FileName);
        }
      }
    }

    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
      if (Config == null || !Config.Dirty) return;

      DialogResult result =
        MessageBox.Show(this,
          "There are unsaved changes in the model. Would you like to save them?",
          "Save changes?",
          MessageBoxButtons.YesNoCancel,
          MessageBoxIcon.Question,
          MessageBoxDefaultButton.Button3);

      if (result == DialogResult.Cancel)
      {
        e.Cancel = true;
      }

      if (result == DialogResult.Yes)
      {
        OnSaveFile(sender, e);
      }
    }

    private void OnParameterValueChanged(object sender, DataGridViewCellEventArgs e)
    {
      if (_isLoading) return;

      if (e.RowIndex < 0) return;

      var row = gridParameters.Rows[e.RowIndex];
      if (row.IsNewRow) return;

      var id = row.Cells[0].Value as string;
      if (string.IsNullOrEmpty(id)) return;

      var value = row.Cells[1].Value as string;
      var data = Config["Reaction"];

      data[id] = value;

      
    }

    EditSpatialSettings Settings { get; set; }

    public void ReadSettings()
    {
      Settings = EditSpatialSettings.GetDefault(Size);

      if (Settings.Width < Screen.FromControl(this).WorkingArea.Width &&
          Settings.Height < Screen.FromControl(this).WorkingArea.Height)
        Size = new Size(Settings.Width, Settings.Height);

    }
    private void OnFormClosed(object sender, FormClosedEventArgs e)
    {
      WriteSettings();
    }

    private void WriteSettings()
    {
      if (Settings != null)
      Settings.Save();
    }

    private void OnEditPreferencesClick(object sender, EventArgs e)
    {
      using (var dlg = new FormSettings { Settings = Settings })
      {
        if (dlg.ShowDialog(this) == DialogResult.OK)
        {
          dlg.Settings.Save();
          ReadSettings();
        }
      }
    }

    private void OnFormLoad(object sender, EventArgs e)
    {
      ReadSettings();
    }

    #region Drag / Drop

    private void MainForm_DragDrop(object sender, DragEventArgs e)
    {
      try
      {
        var sFilenames = (string[])e.Data.GetData(DataFormats.FileDrop);
        var oInfo = new FileInfo(sFilenames[0]);
        if (oInfo.Extension.ToLower() == ".conf")
        {
          OpenFile(sFilenames[0]);
        }
      }
      catch (Exception)
      {
      }
    }

    private void MainForm_DragEnter(object sender, DragEventArgs e)
    {
      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
        var sFilenames = (string[])e.Data.GetData(DataFormats.FileDrop);
        var oInfo = new FileInfo(sFilenames[0]);
        if (oInfo.Extension.ToLower() == ".conf")
        {
          e.Effect = DragDropEffects.Copy;
          return;
        }
      }
      e.Effect = DragDropEffects.None;
    }

    #endregion

  }
}
<file_sep>/WFEditSpatial/Model/SpeciesReferenceInfo.cs
namespace EditSpatial.Model
{
  public class SpeciesReferenceInfo
  {
    public string Id { get; set; }
    public string Compartment { get; set; }
    public double Stoichiometry { get; set; }
  }
}<file_sep>/WFEditSpatial/Controls/BaseSpatialControl.cs
﻿using System;
using System.Windows.Forms;
using EditSpatial.Model;

namespace EditSpatial.Controls
{
  public partial class BaseSpatialControl : UserControl
  {
    public BaseSpatialControl()
    {
      InitializeComponent();
    }

    public bool IsInitializing { get; set; }
    public Action UpdateAction { get; set; }
    
    public event EventHandler ModelChanged;

    public virtual void SaveChanges()
    {
    }

    public virtual void InvalidateSelection()
    {
    }


    protected virtual void OnModelChanged()
    {
      var handler = ModelChanged;
      if (handler != null) handler(this, EventArgs.Empty);
    }
  }

  
}<file_sep>/WFEditSpatial/Controls/ControlSampleFieldGeometry.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using EditSpatial.Model;
using libsbmlcs;
using Image = System.Drawing.Image;

namespace EditSpatial.Controls
{
  public partial class ControlSampleFieldGeometry : BaseSpatialControl
  {
    private SampledField _Data;
    private SampledField _Field;

    public ControlSampleFieldGeometry()
    {
      InitializeComponent();
    }

    public Geometry SpatialGeometry { get; set; }
    public SampledFieldGeometry Current { get; set; }

    public int ThumbSize
    {
      get
      {
        int thumbSize;
        if (int.TryParse(txtSize.Text, out thumbSize))
          return thumbSize;
        return 128;
      }
    }

    public void InitializeFrom(Geometry geometry, string id)
    {
      txtId.Text = id;
      grid.Rows.Clear();
      thumbGeometry.Image = thumbGeometry.InitialImage;
      SpatialGeometry = geometry;
      if (geometry == null) return;

      var sampledFieldGeometry = geometry.getGeometryDefinition(id) as SampledFieldGeometry;
      Current = sampledFieldGeometry;
      if (sampledFieldGeometry == null) return;

      chkIsActive.Checked = Current.getIsActive();

      for (long i = 0; i < sampledFieldGeometry.getNumSampledVolumes(); ++i)
      {
        var vol = sampledFieldGeometry.getSampledVolume(i);
        var spatialId = vol.getId();

        grid.Rows.Add(
          spatialId,
          vol.getDomainType(),
          vol.getSampledValue().ToString(),
          vol.getMinValue().ToString(),
          vol.getMaxValue().ToString()
          );
      }

      _Field = geometry.getSampledField( sampledFieldGeometry.getSampledField());
      _Data = _Field;
      var numSamples = _Field == null ? 0 : _Field.getNumSamples3() - 1;


      if (SpatialGeometry.getNumCoordinateComponents() < 3 || numSamples == 0)
      {
        txtZ.Visible = false;
        trackBar1.Visible = false;
        lblZ.Visible = false;
      }
      else
      {
        txtZ.Visible = true;
        trackBar1.Visible = true;
        lblZ.Visible = true;
        trackBar1.Minimum = 0;
        trackBar1.Maximum = numSamples;
      }


      thumbGeometry.Image = GenerateImage(sampledFieldGeometry, geometry, ThumbSize, ThumbSize);
    }

    public override void SaveChanges()
    {
      if (Current == null || SpatialGeometry == null) return;

      Current.setId(txtId.Text);

      Current.setIsActive(chkIsActive.Checked);

      for (var i = 0; i < grid.Rows.Count && i < Current.getNumSampledVolumes(); ++i)
      {
        var row = grid.Rows[i];
        var current = Current.getSampledVolume(i);
        current.setId((string) row.Cells[0].Value);
        current.setDomainType((string) row.Cells[1].Value);
        double value;
        if (double.TryParse((string) row.Cells[2].Value, out value))
          current.setSampledValue(value);
        if (double.TryParse((string) row.Cells[3].Value, out value))
          current.setMinValue(value);
        if (double.TryParse((string) row.Cells[4].Value, out value))
          current.setMaxValue(value);
      }

      OnModelChanged();
    }

    private List<int> GetUniqueValues(int[] data)
    {
      var result = new List<int>();
      foreach (var t in data.Where(t => !result.Contains(t)))
      {
        result.Add(t);
      }
      return result;
    }

    private Image GenerateImage(SampledFieldGeometry sampledFieldGeometry, Geometry geometry, int resX = 128,
      int resY = 128)
    {
      if (geometry == null || sampledFieldGeometry == null || geometry.getNumCoordinateComponents() < 2)
        return new Bitmap(1, 1);

      try
      {
        if (_Field == null || _Data == null)
          return new Bitmap(1, 1);


        var uncompressedLength = _Data.getUncompressedLength();
        if (uncompressedLength == 0)
          _Data.uncompress();
        uncompressedLength = _Data.getUncompressedLength();
        var array = new int[uncompressedLength];
        _Data.getUncompressed(array);

        var values = GetUniqueValues(array);

        var z = Util.SaveInt(txtZ.Text, 0);
        if (z >= _Field.getNumSamples3())
          z = _Field.getNumSamples3() - 1;
        if (z < 0) z = 0;

        var result = new Bitmap(_Field.getNumSamples1(), _Field.getNumSamples2());
        for (long i = 0; i < _Field.getNumSamples1(); ++i)
        {
          for (long j = 0; j < _Field.getNumSamples2(); ++j)
          {
            var index =
              GetIndexFor(array[i + _Field.getNumSamples1()*j + _Field.getNumSamples1()*_Field.getNumSamples2()*z]);

            result.SetPixel((int) i, (int) j, GetColorForIndex(index));
          }
        }

        return result;
      }
      catch
      {
      }
      return new Bitmap(1, 1);
    }

    private int GetIndexFor(int value)
    {
      if (Current == null) return -1;

      for (long i = 0; i < Current.getNumSampledVolumes(); ++i)
      {
        var current = Current.getSampledVolume(i);
        var currentValue = current.getSampledValue();
        var currentIntValue = (uint) currentValue;
        if (Math.Abs(currentValue - value) < 1e-10)
          return (int) i;
        if (currentIntValue == (byte) value)
          return (int) i;
        if (current.isSetMinValue() && current.isSetMaxValue() &&
            (value >= current.getMinValue() && value <= current.getMaxValue()))
          return (int) i;
      }

      return -1;
    }

    private Color GetColorForIndex(int index)
    {
      switch (index)
      {
        case -1:
          return Color.Transparent;
        default:
        case 0:
          return Color.Black;
        case 1:
          return Color.Red;
        case 2:
          return Color.Blue;
        case 3:
          return Color.Green;
        case 4:
          return Color.Yellow;
        case 5:
          return Color.Goldenrod;
        case 6:
          return Color.Lime;
        case 7:
          return Color.LightBlue;
        case 8:
          return Color.LightSalmon;
        case 9:
          return Color.MediumAquamarine;
      }
    }

    private void OnReorderClick(object sender, EventArgs e)
    {
    }

    public override void InvalidateSelection()
    {
      SpatialGeometry = null;
      Current = null;
      InitializeFrom(null, null);
    }

    private void OnTrackChanged(object sender, EventArgs e)
    {
      if (SpatialGeometry == null)
        return;
      if (SpatialGeometry.getNumCoordinateComponents() < 3)
        return;

      txtZ.Text = trackBar1.Value.ToString();
      if (Current == null)
        return;
      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnUpdateImage(object sender, EventArgs e)
    {
      if (Current == null)
        return;
      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnReorder(object sender, EventArgs e)
    {
      if (Current == null)
        return;

      var vols = new List<SampledVolume>();
      for (var i = (int) Current.getNumSampledVolumes() - 1; i >= 0; --i)
        vols.Add(Current.removeSampledVolume(i));
      foreach (var vol in vols)
      {
        Current.addSampledVolume(vol);
      }


      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnImageLoad(object sender, EventArgs e)
    {
      using (var dialog = new OpenFileDialog {Filter = "Image files|*.tif;*.png|TIFF files|*.tif|PNG files|*.png|All files|*.*" })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
        {
          LoadImage(dialog.FileName);
        }
      }
    }

    private void LoadImage(string fileName)
    {
      var image = Image.FromFile(fileName) as Bitmap;
      if (Current == null)
      {
        Current = SpatialGeometry.createSampledFieldGeometry();
      }
      Current.setId(Path.GetFileNameWithoutExtension(fileName));
      _Field = SpatialGeometry.createSampledField();
      var id = string.Format("sampleFiled_{0}", SpatialGeometry.getNumSampledFields());
      _Field.setId(id);
      Current.setSampledField(id);
      _Field.setNumSamples1(image.Width);
      _Field.setNumSamples2(image.Height);
      _Field.setNumSamples3(1);
      var data = new int[image.Width*image.Height];
      var count = 0;
      for (var i = 0; i < image.Width; ++i)
        for (var j = 0; j < image.Height; ++j)
        {
          var currentColor = image.GetPixel(i, j);

          var value = (int) (currentColor.GetBrightness()*10f);
          //if (currentColor.B > 10 && currentColor.G > 10 && currentColor.R > 10)
          //{ 
          //  value = 1;
          //}
          data[count++] = value;
        }

      var values = GetUniqueValues(data);
      var col = 0;
      foreach (var val in values)
      {
        var vol = Current.createSampledVolume();
        vol.setId(string.Format("vol_{0}", col++));
        vol.setSampledValue(val);
        vol.setMinValue(val);
        vol.setMaxValue(val);
      }

      _Field.setDataType(libsbml.SPATIAL_DATAKIND_UINT8);
      _Field.setCompression(libsbml.SPATIAL_COMPRESSIONKIND_UNCOMPRESSED);
      _Field.setSamples(data, data.Length);
      _Field.setInterpolationType(libsbml.SPATIAL_INTERPOLATIONKIND_NEARESTNEIGHBOR);
      _Field.uncompress();


      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnImageSave(object sender, EventArgs e)
    {
      using (var dialog = new SaveFileDialog {Filter = "TIFF files|*.tif|All files|*.*"})
      {
        if (dialog.ShowDialog() == DialogResult.OK)
        {
          thumbGeometry.Image.Save(dialog.FileName, ImageFormat.Tiff);
        }
      }
    }

    private void OnUserDeletedRow(object sender, DataGridViewRowEventArgs e)
    {
      if (Current == null) return;
      var id = e.Row.Cells[0].Value as string;
      Current.removeSampledVolume(id);
      InitializeFrom(SpatialGeometry, Current.getId());
    }
  }
}<file_sep>/WFEditSpatial/Model/GeometrySettings.cs
using System.Collections.Generic;

namespace EditSpatial.Model
{
  public enum GeometryType
  {
    Default,
    Analytic,
    Sample
  }

  public class GeometrySettings
  {
    public GeometrySettings()
    {
      WrapOutside = false;
      AnalyticDomains = new List<AnalyticSettings>();
      UsedSymbols = new List<string>();
    }

    public double Xmin { get; set; }
    public double Xmax { get; set; }
    public double Ymin { get; set; }
    public double Ymax { get; set; }
    public double Zmin { get; set; }
    public double Zmax { get; set; }
    public List<string> UsedSymbols { get; set; }
    public List<AnalyticSettings> AnalyticDomains { get; set; }
    public GeometryType Type { get; set; }
    public bool WrapOutside { get; set; }
  }
}<file_sep>/WFEditDMP/Forms/FormAdjustDmp.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace WFEditDMP.Forms
{
  public partial class FormAdjustDmp : Form
  {
    public FormAdjustDmp()
    {
      InitializeComponent();
    }

    public DmpModel Model { get; set; }
    public List<double> Numbers { get; set; }

    private void UpdateUI()
    {
      listBox1.Items.Clear();
      if (Numbers != null)
        foreach (var item in Numbers)
        {
          listBox1.Items.Add(item);
        }

      if (Model != null)
      {
        dmpRenderControl1.LoadModel( Model);
        dmpRenderControl1.DisableEditing = true;
      }
    }

    public void InitializeFrom(List<double> selection, DmpModel model)
    {
      Model = model;
      Numbers = selection;
      UpdateUI();
    }

    private void cmdMerge_Click(object sender, EventArgs e)
    {
      if (listBox1.SelectedItems.Count < 2) return;
      var target = (double) listBox1.SelectedItems[0];
      for (var i = listBox1.SelectedItems.Count - 1; i >= 1; i--)
      {
        var current = (double) listBox1.SelectedItems[i];
        Model.Transform(x => x == current ? target : x);
        listBox1.Items.Remove(current);
      }

      Numbers = Model.Range; 
      Model.Min = Numbers.FirstOrDefault();
      Model.Max = Numbers.LastOrDefault();
      UpdateUI();
    }

    private void cmdReplace_Click(object sender, EventArgs e)
    {
      if (listBox1.SelectedItems.Count < 1) return;

      double target;
      if (!double.TryParse(txtReplacement.Text, out target))
        return;

      var list = listBox1.SelectedItems.Cast<double>().ToList();      

      for (var i = list.Count - 1; i >= 0; i--)
      {
        var current = (double)list[i];
        Model.Transform(x => x == current ? target : x);
        listBox1.Items.Remove(current);
        Numbers.Remove(current);
      }


      Numbers = Model.Range;
      Model.Min = Numbers.FirstOrDefault();
      Model.Max = Numbers.LastOrDefault();

      UpdateUI();
    }

    private void OnInvertClick(object sender, EventArgs e)
    {
      Model.Invert();
      UpdateUI();
    }

    private void OnMultiplyClick(object sender, EventArgs e)
    {
      double val;
      if (!double.TryParse(txtScalar.Text, out val)) return;
      
      var list = listBox1.SelectedItems.Cast<double>().ToList();
      if (!list.Any())
        list.AddRange(Numbers);

      Model.Transform(x => list.Contains(x) ?  x * val : x);
      
      Numbers = Model.Range;
      Model.Min = Numbers.FirstOrDefault();
      Model.Max = Numbers.LastOrDefault();
      UpdateUI();
    }

    private void OnMaskClick(object sender, EventArgs e)
    {
      using (var dlg = new OpenFileDialog
      {
        Title = "Choose DMP file to mask with. ",
        Filter = "All supported|*.dmp;*.tif;*.tiff;*.png;*.jpg;*.jpeg;*.bmp|DMP files|*.dmp|Image files|*.tif;*.tiff;*.png;*.jpg;*.jpeg;*.bmp|All files|*.*",
        AutoUpgradeEnabled = true,
      })
      {
        if (dlg.ShowDialog() == DialogResult.OK)
        {
          try
          {
            var file = DmpModel.FromFile(dlg.FileName);
            Model.MaskWith(file);
            Numbers = Model.Range;
            UpdateUI();
          }
          catch
          {
            MessageBox.Show("Masking failed, ensure that the files have precisely the same dimensions", "Masking failed",
              MessageBoxButtons.OK, MessageBoxIcon.Error);
          }
        }
      }
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlCoordinateComponent.cs
﻿using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlCoordinateComponent : BaseSpatialControl
  {
    public ControlCoordinateComponent()
    {
      InitializeComponent();
    }

    private CoordinateComponent Current { get; set; }

    public void InitializeFrom(CoordinateComponent comp)
    {
      Current = comp;
      if (Current == null)
        return;
      txtId.Text = comp.getId();
      txtType.Text = libsbml.CoordinateKind_toString(comp.getType());
      txtIndex.Text = comp.getType().ToString();
      txtUnit.Text = comp.getUnit();

      var min = comp.getBoundaryMin();
      txtMinId.Text = min.getId();
      txtMinValue.Text = min.getValue().ToString();

      var max = comp.getBoundaryMax();
      txtMaxId.Text = max.getId();
      txtMaxValue.Text = max.getValue().ToString();
    }

    public override void SaveChanges()
    {
      if (Current == null) return;

      Current.setId(txtId.Text);
      //Current.setComponentType(txtType.Text);
      Current.setType(Util.SaveInt(txtIndex.Text, Current.getType()));
      if (string.IsNullOrWhiteSpace(txtUnit.Text))
        Current.unsetUnit();
      else
        Current.setUnit(txtUnit.Text);

      var min = Current.getBoundaryMin();
      min.setId(txtMinId.Text);
      min.setValue(Util.SaveDouble(txtMinValue.Text, min.getValue()));

      var max = Current.getBoundaryMax();
      max.setId(txtMaxId.Text);
      max.setValue(Util.SaveDouble(txtMaxValue.Text, max.getValue()));
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(Current);
    }
  }
}<file_sep>/LibEditSpatial/Model/TimeLoopConfig.cs
using System.Collections.Generic;

namespace LibEditSpatial.Model
{
  public class TimeLoopConfig
  {
    public double Time { get; set; }
    public double InitialStep { get; set; }
    public double MinStep { get; set; }
    public double MaxStep { get; set; }
    public double PlotStep { get; set; }
    public int IncreaseRate { get; set; }

    public static TimeLoopConfig Default
    {
      get
      {
        return new TimeLoopConfig
        {
          Time = 10,
          InitialStep = 0.01,
          MinStep = 1e-18,
          MaxStep = 0.01,
          PlotStep = 0.01
        };
      }
    }

    public static TimeLoopConfig FromDict(Dictionary<string, string> dict)
    {
      var result = new TimeLoopConfig
      {
        Time = dict.Get<double>("time"),
        InitialStep = dict.Get<double>("dt"),
        MinStep = dict.Get<double>("dt_min"),
        MaxStep = dict.Get<double>("dt_max"),
        PlotStep = dict.Get<double>("dt_plot"),
        IncreaseRate = dict.Get<int>("increase_rate")
      };
      return result;
    }

    public Dictionary<string, string> ToDict()
    {
      var result = new Dictionary<string, string>();
      result["time"] = Time.ToString();
      result["dt"] = InitialStep.ToString();
      result["dt_min"] = MinStep.ToString();
      result["dt_max"] = MaxStep.ToString();
      result["dt_plot"] = PlotStep.ToString();
      result["increase_rate"] = IncreaseRate.ToString();
      return result;
    }
  }
}<file_sep>/WFEditSpatial/Model/Util.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using EditSpatial.Converter;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using libsbmlcs;
using Ookii.Dialogs;
using SysBio.MathKGI;
using Image = System.Drawing.Image;

namespace EditSpatial.Model
{
  internal static class Util
  {
    public const int CHUNK_SIZE = 2048;

    public static string[][] InbuiltFunctions =
    {
      new[]
      {
        "CIRCLE",
        "lambda(x,y,centerX,centerY,r,piecewise(1, lt(pow(x-centerX, 2) + pow(y-centerY, 2), r*r), 0))",
        "CIRCLE(x,y,25,25,10)",
        "True, if inside circle with given center and radius"
      },
      new[]
      {
        "ELLIPSE",
        "lambda(x,y,centerX,centerY,rx,ry,piecewise(1, lt(pow(x-centerX, 2)/pow(rx,2) + pow(y-centerY, 2)/pow(ry,2), 1), 0))",
        "ELLIPSE(x,y,25,25,10, 10)",
        "True, if inside ellipse with given center and radii"
      },
      new[]
      {
        "RECTANGLE",
        "lambda(x,y,startX, startY, w,h,and(geq(x, startX), leq(x, startX+w), geq(y, startY), leq(y, startY+h)))",
        "RECT(x,y,10,10,10, 10)",
        "Generates a rectangle with given start position and width and height"
      },
      new[]
      {
        "FISH",
        "lambda(x,y,w,h,piecewise(1, ((x - w * 0.42)^2 / (w * 0.37)^2 + (y - h / 2)^2 / (w * 0.25)^2 < 1 || (y < -(w * 0.1) + x && y > w * 1.1 + -x && x < w * 0.9)) && !((x - w * 0.25)^2 / (0.08 * w)^2 + (y - h * 0.45)^2 / (0.08 * h)^2 < 1), 0))",
        "FISH(x,y,width,height)",
        "Generates a fish in the given bounds"
      }
    };

    public static Image GenerateTiffForOrdinal(this AnalyticGeometry analytic, Geometry geometry, int ordinal = 1,
      double z = 1)
    {
      var range1 = geometry.getCoordinateComponent(0);
      var r1Max = range1.getBoundaryMax().getValue();
      var range2 = geometry.getCoordinateComponent(1);
      var r2Max = range2.getBoundaryMax().getValue();

      return analytic.GenerateTiff(geometry, (int) r1Max, (int) r2Max, ordinal, z);
    }

    public static Image GenerateTiff(this AnalyticGeometry analytic, Geometry geometry, int resX = 128, int resY = 128,
      int ordinal = 1, double z = 1)
    {
      if (geometry == null || analytic == null || geometry.getNumCoordinateComponents() < 2)
        return new Bitmap(1, 1);

      try
      {
        var formulas = new List<Tuple<int, ASTNode>>();
        for (long i = 0; i < analytic.getNumAnalyticVolumes(); ++i)
        {
          var current = analytic.getAnalyticVolume(i);
          if (current.getOrdinal() == ordinal)
            formulas.Add(new Tuple<int, ASTNode>((int) current.getOrdinal(), current.getMath()));
        }
        formulas.Sort((a, b) => a.Item1.CompareTo(b.Item1));


        var range1 = geometry.getCoordinateComponent(0);
        var r1Min = range1.getBoundaryMin().getValue();
        var r1Max = range1.getBoundaryMax().getValue();
        var range2 = geometry.getCoordinateComponent(1);
        var r2Min = range2.getBoundaryMin().getValue();
        var r2Max = range2.getBoundaryMax().getValue();

        var depth = geometry.getNumCoordinateComponents() == 3
          ? geometry.getCoordinateComponent(2).getBoundaryMax().getValue()
          : 0;

        var result = new Bitmap(resX, resY);
        for (var i = 0; i < resX; ++i)
        {
          var x = r1Min
                  +
                  (r1Max - r1Min)/
                  resX*i;
          for (var j = 0; j < resY; ++j)
          {
            var y = r2Min
                    +
                    (r2Max - r2Min)/
                    resY*j;

            for (var index = 0; index < formulas.Count; index++)
            {
              var item = formulas[index];
              var isInside = Evaluate(item.Item2,
                new List<string> {"x", "y", "z", "width", "height", "depth"},
                new List<double> {x, y, z, r1Max, r2Max, depth},
                new List<Tuple<string, double>>()
                );
              if (Math.Abs((isInside - 1.0)) < 1E-10)
              {
                result.SetPixel(i, j, ordinal == item.Item1 ? Color.White : Color.Black);
                break;
              }
            }
          }
        }
        return result;
      }
      catch
      {
      }
      return new Bitmap(1, 1);
    }

    public static void ExpandMath(this AnalyticGeometry analytic)
    {
      if (analytic == null) return;

      for (long i = 0; i < analytic.getNumAnalyticVolumes(); ++i)
      {
        var current = analytic.getAnalyticVolume(i);
        if (current == null || !current.isSetMath())
          continue;
        var formula = libsbml.formulaToL3String(current.getMath());

        for (var k = 0; k < InbuiltFunctions.Length; ++k)
        {
          if (formula.Contains(InbuiltFunctions[k][0]))
          {
            var fd = new FunctionDefinition(3, 1);
            fd.setId(InbuiltFunctions[k][0]);
            fd.setMath(libsbml.parseL3Formula(InbuiltFunctions[k][1]));
            SBMLTransforms.replaceFD(current.getMath(), fd);
            fd.Dispose();
          }
        }
      }
    }

    public static List<string> GetCompartmentsFromReaction(Reaction reaction)
    {
      var result = new List<string>();
      if (reaction == null) return result;

      var model = reaction.getModel();
      if (model == null) return result;
      for (var i = 0; i < reaction.getNumReactants(); ++i)
      {
        var reference = reaction.getReactant(i);
        if (reference != null && reference.isSetSpecies())
        {
          var species = model.getSpecies(reference.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          var compartment = species.getCompartment();
          if (!result.Contains(compartment))
            result.Add(compartment);
        }
      }

      for (var i = 0; i < reaction.getNumProducts(); ++i)
      {
        var reference = reaction.getProduct(i);
        if (reference != null && reference.isSetSpecies())
        {
          var species = model.getSpecies(reference.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          var compartment = species.getCompartment();
          if (!result.Contains(compartment))
            result.Add(compartment);
        }
      }

      return result;
    }

    public static Dictionary<string, List<string>> GetSpeciesCompartmentMap(libsbmlcs.Model model, Reaction reaction)
    {
      var result = new Dictionary<string, List<string>>();
      if (reaction == null) return result;

      for (var i = 0; i < reaction.getNumReactants(); ++i)
      {
        var reference = reaction.getReactant(i);
        if (reference != null && reference.isSetSpecies())
        {
          var species = model.getSpecies(reference.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          var compartment = species.getCompartment();
          if (!result.ContainsKey(compartment))
            result[compartment] = new List<string>();
          result[compartment].Add(species.getId());
        }
      }

      for (var i = 0; i < reaction.getNumProducts(); ++i)
      {
        var reference = reaction.getProduct(i);
        if (reference != null && reference.isSetSpecies())
        {
          var species = model.getSpecies(reference.getSpecies());
          if (species == null || !species.isSetCompartment()) continue;
          var compartment = species.getCompartment();
          if (!result.ContainsKey(compartment))
            result[compartment] = new List<string>();
          result[compartment].Add(species.getId());
        }
      }

      return result;
    }

    public static void MoveRuleToAssignment(this libsbmlcs.Model model, string id)
    {
      if (model == null) return;
      var rule = model.getRuleByVariable(id) as AssignmentRule;
      if (rule != null)
      {
        var ia = model.createInitialAssignment();
        ia.setSymbol(rule.getVariable());
        ia.setMath(rule.getMath());
        model.removeRule(rule.getVariable());
      }
    }

    public static AnalyticGeometry GetFirstAnalyticGeometry(this Geometry geometry)
    {
      if (geometry == null) return null;
      for (var i = 0; i < geometry.getNumGeometryDefinitions(); ++i)
      {
        var analytic = geometry.getGeometryDefinition(i) as AnalyticGeometry;
        if (analytic != null) return analytic;
      }
      return null;
    }

    public static SampledFieldGeometry GetFirstSampledFieldGeometry(this Geometry geometry)
    {
      if (geometry == null) return null;
      for (var i = 0; i < geometry.getNumGeometryDefinitions(); ++i)
      {
        var sampledFieldGeometry = geometry.getGeometryDefinition(i) as SampledFieldGeometry;
        if (sampledFieldGeometry != null) return sampledFieldGeometry;
      }
      return null;
    }

    public static void setInitialExpession(this Species species, string expression)
    {
      if (species == null || species.getSBMLDocument() == null || species.getSBMLDocument().getModel() == null)
        return;

      var model = species.getSBMLDocument().getModel();

      var node = libsbml.parseL3Formula(expression);

      if (node == null) return;

      if (node.isNumber())
      {
        if (species.isSetInitialAmount())
          species.setInitialAmount(GetRealValue(node));
        else
          species.setInitialConcentration(GetRealValue(node));
        return;
      }

      var initial = model.getInitialAssignment(species.getId());
      if (initial == null)
      {
        initial = model.createInitialAssignment();
        initial.setSymbol(species.getId());
      }

      initial.setMath(node);
    }

    public static bool IsBasic(this ASTNode node)
    {
      return node.isName() || node.isFunction() ||
             node.isNumber() || node.isConstant() ||
             node.isBoolean() || node.getType() == libsbml.AST_TIMES ||
             node.getType() == libsbml.AST_DIVIDE;
    }

    public static void AppendMorpheusNode(this StringBuilder builder, string format, ASTNode node,
      Dictionary<string, string> map)
    {
      //if (!node.IsBasic())
      //  format = format.Replace("{0}", "({0})");
      builder.AppendFormat(format, MorpheusConverter.TranslateExpression(node, map));
    }

    public static void AppendDuneNode(this StringBuilder builder, string format, ASTNode node,
      Dictionary<string, string> map, libsbmlcs.Model model)
    {
      if (!node.IsBasic())
        format = format.Replace("{0}", "({0})");
      builder.AppendFormat(format, DuneConverter.TranslateExpression(node, map, model));
    }

    private static double GetRealValue(ASTNode node)
    {
      switch (node.getType())
      {
        default:
        case libsbml.AST_REAL:
          return node.getReal();
        case libsbml.AST_REAL_E:
          return Math.Pow(node.getMantissa(), node.getExponent());
        case libsbml.AST_INTEGER:
          return node.getInteger();
        case libsbml.AST_RATIONAL:
          return node.getNumerator()/(double) node.getDenominator();
      }
    }

    public static string getInitialExpession(this Species species)
    {
      var result = "";
      if (species == null || species.getSBMLDocument() == null || species.getSBMLDocument().getModel() == null)
        return result;

      var model = species.getSBMLDocument().getModel();

      var initial = model.getInitialAssignment(species.getId());
      if (initial != null && initial.isSetMath()) return libsbml.formulaToL3String(initial.getMath());

      if (species.isSetInitialAmount())
        return species.getInitialAmount().ToString();

      if (species.isSetInitialConcentration())
        return species.getInitialConcentration().ToString();

      return result;
    }

    public static double? getDiffusionY(this Species species)
    {
      var param = species.getParameterDiffusionY();
      if (param == null) return null;
      return param.getValue();
    }

    public static double? getXMaxBC(this Species species)
    {
      var param = species.getBoundaryCondition("Xmax");
      if (param == null) return null;
      return param.getValue();
    }

    public static double? getYMaxBC(this Species species)
    {
      var param = species.getBoundaryCondition("Ymax");
      if (param == null) return null;
      return param.getValue();
    }

    public static double? getXMinBC(this Species species)
    {
      var param = species.getBoundaryCondition("Xmin");
      if (param == null) return null;
      return param.getValue();
    }

    public static double? getYMinBC(this Species species)
    {
      var param = species.getBoundaryCondition("Ymin");
      if (param == null) return null;
      return param.getValue();
    }

    public static int getBoundaryConditionType(this Parameter param, int defaultValue = libsbml.SPATIAL_BOUNDARYKIND_NEUMANN)
    {
      if (param == null)
        return defaultValue;

      var plugin = param.getPlugin("spatial") as SpatialParameterPlugin;
      if (plugin == null)
        return defaultValue;

      var bc = plugin.getBoundaryCondition();
      if (bc == null)
        return defaultValue;

      return bc.getType();
    }

    public static string getBcType(this Species species)
    {
      var param = species.getBoundaryCondition("Xmax");
      if (param != null)
        return param.getBoundaryConditionType() == libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET
          ? "Dirichlet"
          : "Neumann";
      param = species.getBoundaryCondition("Xmin");
      if (param != null)
        return param.getBoundaryConditionType() == libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET
          ? "Dirichlet"
          : "Neumann";
      param = species.getBoundaryCondition("Ymax");
      if (param != null)
        return param.getBoundaryConditionType() == libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET
          ? "Dirichlet"
          : "Neumann";
      param = species.getBoundaryCondition("Ymin");
      if (param != null)
        return param.getBoundaryConditionType() == libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET
          ? "Dirichlet"
          : "Neumann";
      return "Neumann";
    }

    public static bool IsSpatial(this Parameter parameter)
    {
      var plug = (SpatialParameterPlugin) parameter.getPlugin("spatial");
      if (plug == null) return false;
      return plug.isSpatialParameter();
    }

    public static Parameter setSpatialParameter(this Species species, string id, int typeCode, double value,
      object box = null)
    {
      if (species == null || species.getSBMLDocument() == null || species.getSBMLDocument().getModel() == null)
        return null;

      var model = species.getSBMLDocument().getModel();
      Parameter param = species.getSpatialParameter(typeCode, box);
      if (param == null)
      {
        param = model.createParameter();
        param.initDefaults();
        var plugin = param.getPlugin("spatial") as SpatialParameterPlugin;
        if (plugin != null && box != null)
        {
          switch (typeCode)
          {
            case libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT:
            {
              var diff = plugin.getDiffusionCoefficient();
              if (diff == null)
                diff = plugin.createDiffusionCoefficient();
              diff.setCoordinateReference1((int) box);
              diff.setVariable(species.getId());
              break;
            }
            case libsbml.SBML_SPATIAL_BOUNDARYCONDITION:
            {
              var bc = plugin.getBoundaryCondition();
                if (bc == null)
                  bc = plugin.createBoundaryCondition();
              bc.setVariable(species.getId());
              bc.setCoordinateBoundary((string) box);
              break;
            }
          }
        }
      }

      param.setId(id);
      param.setValue(value);

      return param;
    }

    public static Parameter getSpatialParameter(this Species species, int typeCode, object box = null)
    {
      if (species == null || species.getSBMLDocument() == null || species.getSBMLDocument().getModel() == null)
        return null;

      var model = species.getSBMLDocument().getModel();
      for (var i = 0; i < model.getNumParameters(); ++i)
      {
        var parameter = model.getParameter(i);
        if (parameter == null) continue;
        var plugin = parameter.getPlugin("spatial") as SpatialParameterPlugin;
        if (plugin == null) continue;

        if (plugin.getType() != typeCode) continue;

        if (typeCode == libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT)
        {
          var diff = plugin.getDiffusionCoefficient();
          var index = 0;
          if (box != null && box is int)
            index = (int) box;
          if (diff == null || diff.getCoordinateReference1() != index || diff.getVariable() != species.getId()) continue;
        }
        else if (typeCode == libsbml.SBML_SPATIAL_BOUNDARYCONDITION)
        {
          var bc = plugin.getBoundaryCondition();
          if (bc == null || bc.getVariable() != species.getId()) continue;
          if (box != null && box is string)
          {
            var bound = box as string;
            if (bc.getCoordinateBoundary() != bound) continue;
          }
        }

        return parameter;
      }

      return null;
    }

    public static Parameter getParameterDiffusionX(this Species species)
    {
      return species.getSpatialParameter(libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT, 0);
    }

    public static Parameter getParameterDiffusionY(this Species species)
    {
      return species.getSpatialParameter(libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT, 1);
    }

    public static Parameter getBoundaryCondition(this Species species, string dir)
    {
      return species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, dir);
    }

    public static double? getDiffusionX(this Species species)
    {
      var param = species.getParameterDiffusionX();
      if (param == null) return null;
      return param.getValue();
    }

    internal static byte[] ToBytes(int[] array)
    {
      var result = new byte[array.Length];
      for (var i = 0; i < array.Length; i++)
      {
        result[i] = (byte) (array[i]);
      }
      return result;
    }

    private static int[] ToInt(byte[] array)
    {
      var result = new int[array.Length];
      for (var i = 0; i < array.Length; i++)
      {
        result[i] = array[i];
      }
      return result;
    }

    internal static int[] GetArray(SampledField data)
    {
      switch (data.getDataType())
      {
        case libsbml.SPATIAL_COMPRESSIONKIND_DEFLATED:
        {
          var intValue = new int[data.getSamplesLength()];
          data.getSamples(intValue);
          var bytes = ToBytes(intValue);
          var stream = new MemoryStream(bytes);
          var result = new MemoryStream();
          var zipInputStream = new InflaterInputStream(stream);

          var buffer = new byte[4096]; // 4K is optimum

          StreamUtils.Copy(zipInputStream, result, buffer);
          return ToInt(result.ToArray());
        }
        case libsbml.SPATIAL_COMPRESSIONKIND_UNCOMPRESSED:
        default:
        {
          var intValue = new int[data.getSamplesLength()];
          data.getSamples(intValue);
          return intValue;
        }
      }
    }

    /// <summary>
    ///   Returns the path of the unpacked archive (temp+filename)
    /// </summary>
    /// <param name="archiveFilename">name of the archive file</param>
    /// <param name="deleteIfExists"></param>
    /// <returns>base directory with all the unzipped files</returns>
    public static string UnzipArchive(string archiveFilename, string targetDir, bool deleteIfExists = true)
    {
      using (var inputStream = new FileStream(archiveFilename, FileMode.Open))
      {
        // zipped archive ...
        var stream = new ZipInputStream(inputStream);
        var destination = targetDir;
        if (Directory.Exists(destination) && deleteIfExists)
          try
          {
            Directory.Delete(destination, true);
          }
          catch
          {
          }
        Directory.CreateDirectory(destination);
        ZipEntry entry;
        while ((entry = stream.GetNextEntry()) != null)
        {
          var sName = Path.Combine(destination, entry.Name);
          var dir = Path.GetDirectoryName(sName);
          if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
            Directory.CreateDirectory(dir);
          if (entry.IsDirectory) continue;
          var streamWriter = File.Create(sName);
          var data = new byte[CHUNK_SIZE];
          while (true)
          {
            var size = stream.Read(data, 0, data.Length);
            if (size > 0)
            {
              streamWriter.Write(data, 0, size);
            }
            else
            {
              break;
            }
          }
          streamWriter.Close();
        }
        return destination;
      }
    }

    internal static double ComputeValueForFormula(string formula, List<string> variableIds, List<double> variableData)
    {
      if (formula == null && variableIds.Count > 0) formula = variableIds[0];

      // if we are lucky just return a given variable column
      if (variableIds.Contains(formula))
      {
        var index = variableIds.IndexOf(formula);
        return variableData[index];
      }
      // at this point ... we should try and get a libSBML AST Tree and then evaluate it ...

      var tree = libsbml.parseL3Formula(formula);

      if (tree == null)
        throw new Exception("Invalid MathML in ComputeChange::ComputeValueForFormula");

      return Evaluate(tree, variableIds, variableData, new List<Tuple<string, double>>());
    }

    internal static double Evaluate(ASTNode tree, List<string> sVariableIds, List<double> oVariableData,
      List<Tuple<string, double>> listOfParameters)
    {
      // no data ...
      if (oVariableData == null || oVariableData.Count < 1)
      {
        if (tree.isReal()) return tree.getReal();
        if (tree.isInteger()) return tree.getInteger();
        return 0;
      }

      return EvaluateSingle(tree, sVariableIds, oVariableData, listOfParameters);
    }

    internal static double EvaluateSingle(ASTNode node, List<string> sVariableIds,
      List<double> oVariableData, List<Tuple<string, double>> listOfParameters)
    {
      if (node == null)
        return 0;
      if (node.isReal()) return node.getReal();
      if (node.isInteger()) return node.getInteger();

      if (node.isOperator())
      {
        switch (node.getType())
        {
          case libsbml.AST_PLUS:
            return
              EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters) +
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData, listOfParameters);
          case libsbml.AST_MINUS:
            if (node.getNumChildren() == 1)
              return -
                EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters);
            return
              EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters) -
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData, listOfParameters);
          case libsbml.AST_DIVIDE:
            return
              EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters)/
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData, listOfParameters);
          case libsbml.AST_TIMES:
            return
              EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters)*
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData, listOfParameters);
          case libsbml.AST_POWER:
            return
              Math.Pow(
                EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters),
                EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                  listOfParameters));
          default:
            break;
        }
      }
      else if (node.isName())
      {
        if (sVariableIds.Contains(node.getName()))
        {
          var variableIndex = sVariableIds.IndexOf(node.getName());
          return oVariableData[variableIndex];
        }
        foreach (var param in listOfParameters)
        {
          if (param.Item1 == node.getName())
            return param.Item2;
        }
      }

      // we are still here ... this is bad ... as last measure try a couple of inbuilts
      switch (node.getType())
      {
        case libsbml.AST_FUNCTION_ABS:
          return
            Math.Abs(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCCOS:
          return
            Math.Acos(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCCOSH:
          return
            MathKGI.Acosh(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCCOT:
          return
            MathKGI.Acot(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCCOTH:
          return
            MathKGI.Acoth(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCCSC:
          return
            MathKGI.Acsc(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCCSCH:
          return
            MathKGI.Acsch(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCSEC:
          return
            MathKGI.Asec(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCSECH:
          return
            MathKGI.Asech(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCSIN:
          return
            Math.Asin(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCSINH:
          return
            MathKGI.Asinh(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCTAN:
          return
            Math.Atan(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ARCTANH:
          return
            MathKGI.Atanh(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_CEILING:
          return
            Math.Ceiling(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_COS:
          return
            Math.Cos(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_COSH:
          return
            Math.Cosh(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_COT:
          return
            MathKGI.Cot(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_COTH:
          return
            MathKGI.Coth(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_CSC:
          return
            MathKGI.Csc(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_CSCH:
          return
            MathKGI.Csch(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_DELAY:
          return
            MathKGI.Delay(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        case libsbml.AST_FUNCTION_EXP:
          return
            Math.Exp(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_FACTORIAL:
          return
            MathKGI.Factorial(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_FLOOR:
          return
            Math.Floor(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_LN:
          return
            Math.Log(
              EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData, listOfParameters),
              Math.E);
        case libsbml.AST_FUNCTION_LOG:
          return
            Math.Log10(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_PIECEWISE:
        {
          var numChildren = (int) node.getNumChildren();
          var temps = new double[numChildren];
          for (var i = 0; i < numChildren; i++)
          {
            temps[i] = EvaluateSingle(node.getChild(i), sVariableIds, oVariableData,
              listOfParameters);
          }
          return Piecewise(temps);
          // MathKGI.Piecewise(temps);
        }
        case libsbml.AST_FUNCTION_POWER:
          return Math.Pow(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters),
            EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_ROOT:
          return MathKGI.Root(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters),
            EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
              listOfParameters));
        case libsbml.AST_FUNCTION_SEC:
          return MathKGI.Sec(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters));
        case libsbml.AST_FUNCTION_SECH:
          return MathKGI.Sech(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters));
        case libsbml.AST_FUNCTION_SIN:
          return Math.Sin(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters));
        case libsbml.AST_FUNCTION_SINH:
          return Math.Sinh(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters));
        case libsbml.AST_FUNCTION_TAN:
          return Math.Tan(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters));
        case libsbml.AST_FUNCTION_TANH:
          return Math.Tanh(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
            listOfParameters));
        case libsbml.AST_LOGICAL_AND:
        {
          var numChildren = (int) node.getNumChildren();
          var temps = new double[numChildren];
          for (var i = 0; i < numChildren; i++)
          {
            temps[i] = EvaluateSingle(node.getChild(i), sVariableIds, oVariableData,
              listOfParameters);
          }
          return
            MathKGI.And(temps);
        }
        case libsbml.AST_LOGICAL_NOT:
        {
          return
            MathKGI.Not(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters));
        }
        case libsbml.AST_LOGICAL_OR:
        {
          var numChildren = (int) node.getNumChildren();
          var temps = new double[numChildren];
          for (var i = 0; i < numChildren; i++)
          {
            temps[i] = EvaluateSingle(node.getChild(i), sVariableIds, oVariableData,
              listOfParameters);
          }
          return
            MathKGI.Or(temps);
        }
        case libsbml.AST_LOGICAL_XOR:
        {
          var numChildren = (int) node.getNumChildren();
          var temps = new double[numChildren];
          for (var i = 0; i < numChildren; i++)
          {
            temps[i] = EvaluateSingle(node.getChild(i), sVariableIds, oVariableData,
              listOfParameters);
          }
          return
            MathKGI.Xor(temps);
        }
        case libsbml.AST_RELATIONAL_EQ:
        {
          return
            MathKGI.Eq(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        }
        case libsbml.AST_RELATIONAL_GEQ:
          return
            MathKGI.Geq(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        case libsbml.AST_RELATIONAL_GT:
          return
            MathKGI.Gt(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        case libsbml.AST_RELATIONAL_LEQ:
          return
            MathKGI.Leq(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        case libsbml.AST_RELATIONAL_LT:
          return
            MathKGI.Lt(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        case libsbml.AST_RELATIONAL_NEQ:
          return
            MathKGI.Neq(EvaluateSingle(node.getLeftChild(), sVariableIds, oVariableData,
              listOfParameters),
              EvaluateSingle(node.getRightChild(), sVariableIds, oVariableData,
                listOfParameters));
        default:
          break;
      }

      return 0;
    }

    public static double Piecewise(double[] args)
    {
      double result;
      for (var i = 0; i < args.Length - 1; i += 2)
      {
        var num = args[i + 1];
        if (num == 1.0)
        {
          result = args[i];
          return result;
        }
      }
      result = args[args.Length - 1];

      return result;
    }

    public static double SaveDouble(string value, double defaultValue = 0)
    {
      double val;
      if (double.TryParse(value, out val))
        return val;
      return defaultValue;
    }

    public static long SaveInt(string value, long defaultValue)
    {
      long val;
      if (long.TryParse(value, out val))
        return val;
      return defaultValue;
    }

    public static int SaveInt(string value, int defaultValue)
    {
      int val;
      if (int.TryParse(value, out val))
        return val;
      return defaultValue;
    }

    

    public static double GetDouble(this DataGridViewCell cell, double defaultValue = 0)
    {
      var value = cell.Value;
      if (value.GetType() == typeof (string))
      {
        return SaveDouble((string) value, defaultValue);
      }

      if (value.GetType() == typeof (double) || value.GetType() == typeof (int))
        return (double) value;

      return defaultValue;
    }

    public static CompartmentMapping getCompartmentMapping(this Compartment comp)
    {
      if (comp == null) return null;
      var cplug = (SpatialCompartmentPlugin) comp.getPlugin("spatial");
      if (cplug == null) return null;
      var map = cplug.getCompartmentMapping();
      if (map == null)
        map = cplug.createCompartmentMapping();

      return map;
    }

    public static int GetNumAnalyticGeometries(this Geometry geom)
    {
      if (geom == null) return 0;

      var count = 0;

      for (var i = 0; i < geom.getNumGeometryDefinitions(); ++i)
      {
        var current = geom.getGeometryDefinition(i);
        if (current is AnalyticGeometry) ++count;
      }

      return count;
    }

    public static int GetNumSampledFieldGeometries(this Geometry geom)
    {
      if (geom == null) return 0;

      var count = 0;

      for (var i = 0; i < geom.getNumGeometryDefinitions(); ++i)
      {
        var current = geom.getGeometryDefinition(i);
        if (current is SampledFieldGeometry) ++count;
      }

      return count;
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlAnalyticGeometry.cs
﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Forms;
using EditSpatial.Model;
using libsbmlcs;
using LibEditSpatial.Model;
using Image = System.Drawing.Image;

namespace EditSpatial.Controls
{
  public partial class ControlAnalyticGeometry : BaseSpatialControl
  {
    public ControlAnalyticGeometry()
    {
      InitializeComponent();
      IsInitializing = false;
      RowsAdded = new List<int>();
    }

    public List<int> RowsAdded { get; set; }
    public Geometry SpatialGeometry { get; set; }
    public AnalyticGeometry Current { get; set; }

    public int ThumbSize
    {
      get
      {
        int thumbSize;
        if (int.TryParse(txtSize.Text, out thumbSize))
          return thumbSize;
        return 128;
      }
    }

    public double CurrentZ
    {
      get
      {
        double value;
        if (double.TryParse(txtZ.Text, out value))
          return value;
        return 0;
      }
    }

    private void CommitAddedRows(AnalyticGeometry analytic)
    {
      if (RowsAdded.Count > 0 && analytic != null)
      {
        for (var i = RowsAdded.Count - 1; i >= 0; i--)
        {
          var row = grid.Rows[RowsAdded[i]];
          var vol = analytic.createAnalyticVolume();
          vol.setId(row.Cells[0].Value as string);
          vol.setFunctionType(libsbml.FunctionKind_fromString(row.Cells[1].Value as string));
          long ordinal = 0;
          vol.setOrdinal((int)ordinal);
          if (long.TryParse(row.Cells[2].Value as string, out ordinal))
            vol.setOrdinal((int)ordinal);
          vol.setDomainType(row.Cells[3].Value as string);
          vol.setMath(libsbml.parseL3Formula(row.Cells[4].Value as string));
          RowsAdded.RemoveAt(i);
        }
      }
    }

    public void InitializeFrom(Geometry geometry, AnalyticGeometry analytic)
    {
      IsInitializing = true;
      try
      {
        if (geometry == null)
        {
          txtId.Text = "";
          grid.Rows.Clear();
          SpatialGeometry = geometry;
          thumbGeometry.Image = thumbGeometry.InitialImage;
          return;
        }

        CommitAddedRows(analytic);

        grid.Rows.Clear();
        SpatialGeometry = geometry;
        thumbGeometry.Image = thumbGeometry.InitialImage;
        Current = analytic;
        if (analytic == null) return;

        chkIsActive.Checked = analytic.getIsActive();
        txtId.Text = analytic.getId();

        for (long i = 0; i < analytic.getNumAnalyticVolumes(); ++i)
        {
          var vol = analytic.getAnalyticVolume(i);
          var spatialId = vol.getId();
          grid.Rows.Add(spatialId, libsbml.FunctionKind_toString(vol.getFunctionType()), vol.getOrdinal().ToString(), vol.getDomainType(),
            libsbml.formulaToL3String(vol.getMath()));
        }

        if (SpatialGeometry.getNumCoordinateComponents() < 3)
        {
          txtZ.Visible = false;
          trackBar1.Visible = false;
          lblZ.Visible = false;
        }
        else
        {
          txtZ.Visible = true;
          trackBar1.Visible = true;
          lblZ.Visible = true;
        }

        thumbGeometry.Image = GenerateImage(analytic, geometry, ThumbSize, ThumbSize);
      }
      finally
      {
        IsInitializing = false;
      }
    }

    public void InitializeFrom(Geometry geometry, string id)
    {
      if (geometry == null)
      {
        txtId.Text = id;
        grid.Rows.Clear();
        SpatialGeometry = geometry;
        thumbGeometry.Image = thumbGeometry.InitialImage;
        return;
      }

      var analytic = geometry.getGeometryDefinition(id) as AnalyticGeometry;
      InitializeFrom(geometry, analytic);
    }

    public override void SaveChanges()
    {
      if (Current == null || SpatialGeometry == null) return;

      Current.setId(txtId.Text);

      Current.setIsActive(chkIsActive.Checked);

      CommitAddedRows(Current);

      for (var i = 0; i < grid.Rows.Count && i < Current.getNumAnalyticVolumes(); ++i)
      {
        var row = grid.Rows[i];
        if (row.IsNewRow) continue;
        var current = Current.getAnalyticVolume(i);
        current.setId((string) row.Cells[0].Value);
        current.setFunctionType(libsbml.FunctionKind_fromString((string) row.Cells[1].Value));
        current.setOrdinal((int)Util.SaveInt((string) row.Cells[2].Value, 0L));
        current.setDomainType((string) row.Cells[3].Value);
        current.setMath(libsbml.parseL3Formula((string) row.Cells[4].Value));
      }

      Current.ExpandMath();
      OnModelChanged();
    }

    private DmpModel GenerateDmp(int thumbSize)
    {
      if (Current == null || SpatialGeometry == null) return null;

      try
      {
        Current.ExpandMath();

        var formulas = new List<Tuple<int, ASTNode>>();
        for (long i = 0; i < Current.getNumAnalyticVolumes(); ++i)
        {
          var current = Current.getAnalyticVolume(i);
          formulas.Add(new Tuple<int, ASTNode>((int) current.getOrdinal(), current.getMath()));
        }
        formulas.Sort((a, b) => b.Item1.CompareTo(a.Item1));

        var range1 = SpatialGeometry.getCoordinateComponent(0);
        var r1Min = range1.getBoundaryMin().getValue();
        var r1Max = range1.getBoundaryMax().getValue();
        var range2 = SpatialGeometry.getCoordinateComponent(1);
        var r2Min = range2.getBoundaryMin().getValue();
        var r2Max = range2.getBoundaryMax().getValue();

        var r3Max = SpatialGeometry.getNumCoordinateComponents() == 3
          ? SpatialGeometry.getCoordinateComponent(2).getBoundaryMax().getValue()
          : 0;
        var r3Min = SpatialGeometry.getNumCoordinateComponents() == 3
          ? SpatialGeometry.getCoordinateComponent(2).getBoundaryMin().getValue()
          : 0;

        var resX = thumbSize;
        var resY = thumbSize;

        var result = new DmpModel(resX, resY)
        {
          MinX = r1Min,
          MaxX = r1Max,
          MinY = r2Min,
          MaxY = r2Max
        };

        for (var nX = 0; nX < resX; ++nX)
        {
          var x = r1Min
                  +
                  (r1Max - r1Min)/
                  resX*nX;
          for (var nY = 0; nY < resY; ++nY)
          {
            var y = r2Min
                    +
                    (r2Max - r2Min)/
                    resY*nY;

            for (var index = 0; index < formulas.Count; index++)
            {
              var item = formulas[index];
              var isInside = Util.Evaluate(item.Item2,
                new List<string>
                {
                  "x",
                  "y",
                  "z",
                  "width",
                  "height",
                  "depth",
                  "Xmin",
                  "Xmax",
                  "Ymin",
                  "Ymax",
                  "Zmin",
                  "Zmax"
                },
                new List<double> {x, y, CurrentZ, r1Max, r2Max, r3Max, r1Min, r1Max, r2Min, r2Max, r3Min, r3Max},
                new List<Tuple<string, double>>()
                );
              if (Math.Abs((isInside - 1.0)) < 1E-10)
              {
                result[nX, nY] = item.Item1;
                break;
              }
            }
          }
        }
        return result;
      }
      catch
      {
      }

      return null;
    }

    private void ExportDmp(string fileName, int thumbSize)
    {
      var dmp = GenerateDmp(thumbSize);
      if (dmp != null)
        dmp.SaveAs(fileName);
    }

    private Image GenerateImage(AnalyticGeometry analytic, Geometry geometry, int resX = 128, int resY = 128)
    {
      if (geometry == null || analytic == null || geometry.getNumCoordinateComponents() < 2)
        return new Bitmap(1, 1);

      try
      {
        Current.ExpandMath();

        var formulas = new List<Tuple<int, ASTNode>>();
        for (long i = 0; i < analytic.getNumAnalyticVolumes(); ++i)
        {
          var current = analytic.getAnalyticVolume(i);
          formulas.Add(new Tuple<int, ASTNode>((int) current.getOrdinal(), current.getMath()));
        }
        formulas.Sort((a, b) => b.Item1.CompareTo(a.Item1));

        var range1 = geometry.getCoordinateComponent(0);
        var r1Min = range1.getBoundaryMin().getValue();
        var r1Max = range1.getBoundaryMax().getValue();
        var range2 = geometry.getCoordinateComponent(1);
        var r2Min = range2.getBoundaryMin().getValue();
        var r2Max = range2.getBoundaryMax().getValue();

        var r3Max = geometry.getNumCoordinateComponents() == 3
          ? geometry.getCoordinateComponent(2).getBoundaryMax().getValue()
          : 0;
        var r3Min = geometry.getNumCoordinateComponents() == 3
          ? geometry.getCoordinateComponent(2).getBoundaryMin().getValue()
          : 0;


        var result = new Bitmap(resX, resY);
        for (var i = 0; i < resX; ++i)
        {
          var x = r1Min
                  +
                  (r1Max - r1Min)/
                  resX*i;
          for (var j = 0; j < resY; ++j)
          {
            var y = r2Min
                    +
                    (r2Max - r2Min)/
                    resY*j;

            for (var index = 0; index < formulas.Count; index++)
            {
              var item = formulas[index];
              var isInside = Util.Evaluate(item.Item2,
                new List<string>
                {
                  "x",
                  "y",
                  "z",
                  "width",
                  "height",
                  "depth",
                  "Xmin",
                  "Xmax",
                  "Ymin",
                  "Ymax",
                  "Zmin",
                  "Zmax"
                },
                new List<double> {x, y, CurrentZ, r1Max, r2Max, r3Max, r1Min, r1Max, r2Min, r2Max, r3Min, r3Max},
                new List<Tuple<string, double>>()
                );
              if (Math.Abs((isInside - 1.0)) < 1E-10)
              {
                result.SetPixel(i, j, GetColorForIndex(item.Item1));
                break;
              }
            }
          }
        }
        return result;
      }
      catch
      {
      }
      return new Bitmap(1, 1);
    }

    private Color GetColorForIndex(int index)
    {
      switch (index)
      {
        default:
        case 0:
          return Color.Black;
        case 1:
          return Color.Red;
        case 2:
          return Color.Blue;
        case 3:
          return Color.Green;
        case 4:
          return Color.Chocolate;
        case 5:
          return Color.SlateGray;
      }
    }

    private void FlipOrder(AnalyticGeometry geometry)
    {
      if (geometry == null)
        return;
      if (geometry.getNumAnalyticVolumes() == 0)
        return;
      var list = new List<Tuple<int, int>>();
      for (long i = 0; i < geometry.getNumAnalyticVolumes(); ++i)
      {
        var current = geometry.getAnalyticVolume(i);
        list.Add(new Tuple<int, int>((int) i, (int) current.getOrdinal()));
      }

      var ordered = list.OrderByDescending(item => item.Item2);
      foreach (var item in ordered)
      {
        var current = geometry.getAnalyticVolume(item.Item1);
        current.setOrdinal(((int)geometry.getNumAnalyticVolumes() - 1) - item.Item2);
      }
    }

    private void SortedOrder(AnalyticGeometry geometry)
    {
      if (geometry == null)
        return;
      if (geometry.getNumAnalyticVolumes() == 0)
        return;
      var list = new List<Tuple<int, int, string>>();
      for (long i = 0; i < geometry.getNumAnalyticVolumes(); ++i)
      {
        var current = geometry.getAnalyticVolume(i);
        list.Add(new Tuple<int, int, string>((int) i, (int) current.getOrdinal(), current.getId()));
      }

      var ordered = list.OrderByDescending(item => item.Item2);
      var volList = geometry.getListOfAnalyticVolumes();
      foreach (var item in ordered)
      {
        var vol = volList.remove(item.Item3);
        volList.insert(item.Item2, vol);
      }
    }

    private void OnReorderClick(object sender, EventArgs e)
    {
      if (Current == null)
        return;

      FlipOrder(Current);
      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnSortClick(object sender, EventArgs e)
    {
      if (Current == null)
        return;

      SortedOrder(Current);
      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnUpdateImage(object sender, EventArgs e)
    {
      if (Current == null)
        return;

      IsInitializing = true;
      SaveChanges();
      IsInitializing = false;
      InitializeFrom(SpatialGeometry, Current.getId());
    }

    private void OnTrackChanged(object sender, EventArgs e)
    {
      if (SpatialGeometry == null)
        return;
      if (SpatialGeometry.getNumCoordinateComponents() < 3)
        return;

      var range1 = SpatialGeometry.getCoordinateComponent(2);
      var r1Min = range1.getBoundaryMin().getValue();
      var r1Max = range1.getBoundaryMax().getValue();
      var x = r1Min
              +
              (r1Max - r1Min)/
              trackBar1.Maximum*trackBar1.Value;
      txtZ.Text = x.ToString();
      if (Current == null)
        return;
      InitializeFrom(SpatialGeometry, Current.getId());
    }

    public override void InvalidateSelection()
    {
      Current = null;
      SpatialGeometry = null;
      InitializeFrom(null, string.Empty);
    }

    private void OnExportTiffClick(object sender, EventArgs e)
    {
      if (grid.SelectedRows.Count == 0) return;
      long ordinal = 0;
      long.TryParse(grid.SelectedRows[0].Cells[2].Value as string, out ordinal);

      var range1 = SpatialGeometry.getCoordinateComponent(0);
      var r1Min = range1.getBoundaryMin().getValue();
      var r1Max = range1.getBoundaryMax().getValue();
      var range2 = SpatialGeometry.getCoordinateComponent(1);
      var r2Min = range2.getBoundaryMin().getValue();
      var r2Max = range2.getBoundaryMax().getValue();

      var image = Current.GenerateTiff(SpatialGeometry, (int) r1Max, (int) r2Max, (int) ordinal, CurrentZ);

      using (var dialog = new SaveFileDialog {Filter = "TIFF files|*.tif|All files|*.*"})
      {
        if (dialog.ShowDialog() == DialogResult.OK)
        {
          image.Save(dialog.FileName, ImageFormat.Tiff);
        }
      }
    }

    private void OnRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
    {
      if (Current == null || IsInitializing || e.RowIndex - 1 < 0) return;

      RowsAdded.Add(e.RowIndex - 1);
    }

    private void OnMakeFirstClick(object sender, EventArgs e)
    {
      if (Current == null || IsInitializing || grid.SelectedRows.Count != 1) return;

      var selected = grid.SelectedRows[0].Cells[0].Value as string;
      for (var i = 0; i < Current.getNumAnalyticVolumes(); ++i)
      {
        var current = Current.getAnalyticVolume(i);
        if (current == null) continue;
        if (current.getId() != selected) continue;

        Current.removeAnalyticVolume(i);
        Current.getListOfAnalyticVolumes().insertAndOwn(0, current);
        InitializeFrom(SpatialGeometry, Current.getId());
        break;
      }
    }

    private void cmdExport_Click(object sender, EventArgs e)
    {
      using (var dlg = new SaveFileDialog {Title = "Export DMP", Filter = "DMP files|*.dmp|All files|*.*"})
      {
        if (dlg.ShowDialog() == DialogResult.OK)
          ExportDmp(dlg.FileName, ThumbSize);
      }
    }
  }
}<file_sep>/LibEditSpatial/Model/VoidStringDelegate.cs
namespace LibEditSpatial.Model
{
  public delegate void VoidStringDelegate(string data);
}<file_sep>/WFEditSpatial/Controls/ControlText.cs
﻿using System.Windows.Forms;

namespace EditSpatial.Controls
{
  public partial class ControlText : TextBox
  {
    public ControlText()
    {
      InitializeComponent();
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlCoordinateComponents.cs
﻿using System.Windows.Forms;
using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlCoordinateComponents : BaseSpatialControl
  {
    public ControlCoordinateComponents()
    {
      InitializeComponent();
    }

    public Geometry Current { get; set; }

    public void InitializeFrom(Geometry geometry)
    {
      while (tblLayout.Controls.Count > 1)
        tblLayout.Controls.RemoveAt(1);

      // remove old ones
      while (tblLayout.RowStyles.Count > 1)
        tblLayout.RowStyles.RemoveAt(1);

      Current = geometry;
      if (geometry == null) return;
      txtCoordSystem.Text = libsbml.GeometryKind_toString(geometry.getCoordinateSystem());

      for (long i = 0; i < geometry.getNumCoordinateComponents(); ++i)
      {
        tblLayout.RowStyles.Add(new RowStyle
        {
          SizeType = SizeType.AutoSize
        });

        var current = geometry.getCoordinateComponent(i);
        var control = new ControlCoordinateComponent {Dock = DockStyle.Fill};
        control.InitializeFrom(current);
        tblLayout.Controls.Add(control, 0, ((int) i) + 1);
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;
      Current.setCoordinateSystem(txtCoordSystem.Text);

      foreach (var control in tblLayout.Controls)
      {
        var current = control as BaseSpatialControl;
        if (current == null) continue;
        current.SaveChanges();
      }
      OnModelChanged();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }
  }
}<file_sep>/LibEditSpatial/pvrender.py
# simple paraview python script for generating screenshots
from paraview.simple import *
import sys

def main(args):  
   if (len(args) != 4):
      print("Usage: renderFile filename variable output")
      return 1
   filename = args[1]
   variable = args[2]
   result = args[3]
   renderFile(filename, variable, result)
  
def renderFile(fileName, variable, output):
  #read a file
  reader = XMLUnstructuredGridReader(FileName=fileName)
  
  # select variable
  data = reader.PointData[variable]
  range = data.GetRange()
  reader.PointArrayStatus = [variable]
  
  #position camera
  view = GetActiveView()
  if not view:
      view = CreateRenderView()  
    
  #lut, pws= return_colours(variable)
  dp = GetDisplayProperties()
  
  dp.PointSize = 2
  dp.Representation = "Surface"
  dp.ColorArrayName = variable
  
  dp.LookupTable = MakeBlueToRedLT(range[0], range[1])
  dp.LookupTable.ColorSpace = 'Diverging'
  dp.LookupTable.VectorMode = 'Magnitude' 
  
  bar = CreateScalarBar(LookupTable=dp.LookupTable, Title=variable, TitleFontSize = 10, LabelFontSize = 10)
  bar.AutomaticLabelFormat = 0
  bar.LabelFormat = '%6.3f'
  bar.RangeLabelFormat = '%6.3f'
  bar.Title = variable
  bar.ComponentTitle = ''
  view.Representations.append(bar)

  #draw the object
  Show()

  # render 
  Render()
   
  #save screenshot
  SaveScreenshot(output)

if __name__ == '__main__':
  main(sys.argv)
<file_sep>/LibEditSpatial/Model/EditSpatialSettings.cs
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;

namespace LibEditSpatial.Model
{
  [Serializable]
  public class EditSpatialSettings
  {
    public string LastDir { get; set; }
    public string CygwinDir { get; set; }
    public string DuneDir { get; set; }
    public string ParaViewDir { get; set; }
    public string DefaultDir { get; set; }
    public bool IgnoreMultiCompartments { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }

    private static EditSpatialSettings _Instance;
    public static EditSpatialSettings Instance
    {
      get
      {
        if (_Instance == null)
          _Instance = GetDefault(new Size(1000, 600));
        return _Instance;
      }
      set
      {
        _Instance = value;
        _Instance.Save();
      }
    }

    public static string ConfigFile
    {
      get
      {
        return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "spatial_tools.xml");
      }
    }

    public void WriteXmlToFile(string fileName)
    {
      var serializer = new System.Xml.Serialization.XmlSerializer(this.GetType());
      using (var stream = new StringWriter())
      {
        serializer.Serialize(stream, this);
        stream.Flush();
        File.WriteAllText(fileName, stream.ToString());
      }
    }


    public static EditSpatialSettings FromXmlFile(string fileName)
    {
      try
      {
        var serializer = new System.Xml.Serialization.XmlSerializer(typeof(EditSpatialSettings));
        var text = File.ReadAllText(fileName);
        using (var stringReader = new StringReader(text))
        {
          return (EditSpatialSettings)serializer.Deserialize(stringReader);
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine("Could not load settings: " + ex.Message + "\n\n" + ex.StackTrace);
        return null;
      }
    }

    public static EditSpatialSettings GetDefault(Size current)
    {
      var key = FromXmlFile(ConfigFile);
      if (key == null)
        return new EditSpatialSettings
        {
          Height = current.Height,
          Width = current.Width
        };
      //var result = new EditSpatialSettings
      //{
      //  LastDir = (string) key.GetValue("lastDir", ""),
      //  CygwinDir = (string) key.GetValue("cygwinDir", ""),
      //  DuneDir = (string) key.GetValue("duneDir", ""),
      //  ParaViewDir = (string) key.GetValue("paraviewDir", ""),
      //  DefaultDir = (string) key.GetValue("defaultDir", ""),
      //  IgnoreMultiCompartments = (int) key.GetValue("ignoreComps", 0) == 1,
      //  Height = (int) key.GetValue("height", current.Height),
      //  Width = (int) key.GetValue("width", current.Width)
      //};
      return key;
    }

    public void Save()
    {
      WriteXmlToFile(ConfigFile);
      //var key = Application.UserAppDataRegistry;
      //if (key == null) return;
      //
      //key.SetValue("lastDir", LastDir);
      //key.SetValue("cygwinDir", CygwinDir);
      //key.SetValue("duneDir", DuneDir);
      //key.SetValue("defaultDir", DefaultDir);
      //key.SetValue("paraviewDir", ParaViewDir);
      //key.SetValue("ignoreComps", IgnoreMultiCompartments ? 1 : 0);
      //key.SetValue("height", Height);
      //key.SetValue("width", Width);
    }
  }
}<file_sep>/LibEditSpatial/Controls/CtrlNewton.cs
﻿using System;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Controls
{
  public partial class CtrlNewton : UserControl
  {
    public CtrlNewton()
    {
      InitializeComponent();
    }

    public NewtonConfig Model { get; set; }

    public void LoadModel(NewtonConfig model)
    {
      Model = model;

      txtLinearVerbosity.Text = model.LinearVerbosity.ToString();
      txtReassembleThreshold.Text = model.ReassembleThreshold.ToString();
      txtLineSearchMaxIterations.Text = model.LineSearchMaxIterations.ToString();
      txtMaxIterations.Text = model.MaxIterations.ToString();
      txtAbsoluteLimit.Text = model.AbsoluteLimit.ToString();
      txtReduction.Text = model.Reduction.ToString();
      txtLinearReduction.Text = model.LinearReduction.ToString();
      txtLineSearchDampingFactor.Text = model.LineSearchDampingFactor.ToString();
      txtVerbosity.Text = model.Verbosity.ToString();
    }

    private void txtVerbosity_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtVerbosity.Text, out temp))
        Model.Verbosity = temp;
    }

    private void txtLineSearchDampingFactor_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtLineSearchDampingFactor.Text, out temp))
        Model.LineSearchDampingFactor = temp;
    }

    private void txtLinearReduction_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtLinearReduction.Text, out temp))
        Model.LinearReduction = temp;
    }

    private void txtReduction_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtReduction.Text, out temp))
        Model.Reduction = temp;
    }

    private void txtAbsoluteLimit_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtAbsoluteLimit.Text, out temp))
        Model.AbsoluteLimit = temp;
    }

    private void txtMaxIterations_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtMaxIterations.Text, out temp))
        Model.MaxIterations = temp;
    }

    private void txtLineSearchMaxIterations_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtLineSearchMaxIterations.Text, out temp))
        Model.LineSearchMaxIterations = temp;
    }

    private void txtReassembleThreshold_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtReassembleThreshold.Text, out temp))
        Model.ReassembleThreshold = temp;
    }

    private void txtLinearVerbosity_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtLinearVerbosity.Text, out temp))
        Model.LinearVerbosity = temp;
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlCompartment.cs
﻿using System.Collections.Generic;
using System.Windows.Forms;
using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlCompartment : BaseSpatialControl
  {
    public ControlCompartment()
    {
      InitializeComponent();
      RowsAdded = new List<int>();
    }

    private SBMLDocument Current { get; set; }
    public List<int> RowsAdded { get; set; }

    public void InitializeFrom(SBMLDocument document)
    {
      grid.Rows.Clear();
      Current = document;
      if (document == null || document.getModel() == null) return;
      try
      {
        IsInitializing = true;
        var model = document.getModel();

        CommitAddedRows(Current);
        for (long i = 0; i < model.getNumCompartments(); ++i)
        {
          var current = model.getCompartment(i);
          grid.Rows.Add(current.getId(),
            current.getName(),
            current.getSize().ToString(),
            current.getSpatialDimensionsAsDouble().ToString()
            );
        }
      }
      finally
      {
        IsInitializing = false;
      }
    }

    private void CommitAddedRows(SBMLDocument current)
    {
      if (RowsAdded.Count > 0 && current != null)
      {
        for (var i = RowsAdded.Count - 1; i >= 0; i--)
        {
          try
          {
            var row = grid.Rows[RowsAdded[i]];
            var comp = current.getModel().createCompartment();
            comp.initDefaults();
            comp.setId(row.Cells[0].Value as string);
            comp.setName(row.Cells[1].Value as string);
            double value;
            if (double.TryParse((string) row.Cells[2].Value, out value))
              comp.setSize(value);
            if (double.TryParse((string) row.Cells[3].Value, out value))
              comp.setSpatialDimensions(value);
            RowsAdded.RemoveAt(i);
          }
          catch
          {
          }
        }
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;

      CommitAddedRows(Current);

      for (var i = 0; i < grid.Rows.Count; ++i)
      {
        var row = grid.Rows[i];
        var current = Current.getModel().getCompartment((string) row.Cells[0].Value);
        if (current == null) continue;

        current.setName(row.Cells[1].Value as string);
        double value;
        if (double.TryParse((string) row.Cells[2].Value, out value))
          current.setSize(value);
        if (double.TryParse((string) row.Cells[3].Value, out value))
          current.setSpatialDimensions(value);
      }

      OnModelChanged();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }

    private void OnUserDeletedRow(object sender, DataGridViewRowEventArgs e)
    {
      if (Current == null) return;
      var id = e.Row.Cells[0].Value as string;
      Current.getModel().removeParameter(id);
      InitializeFrom(Current);
    }

    private void OnRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
    {
      if (Current == null || IsInitializing || e.RowIndex == 0) return;

      RowsAdded.Add(e.RowIndex - 1);
    }
  }
}<file_sep>/LibEditSpatial/Controls/DmpRenderControl.cs
﻿using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Controls
{
  public partial class DmpRenderControl : UserControl
  {
    public DmpRenderControl()
    {
      InitializeComponent();
      Painting = false;
      CurrentValue = 10;
      PencilSize = 1;
    }

    public DmpModel Model { get; set; }
    public double CurrentValue { get; set; }
    private bool Painting { get; set; }
    public int PencilSize { get; set; }
    public bool DisableEditing { get; set; }
    public bool DisableNotification { get; set; }
    public event EventHandler<Point> IndexLocationChanged;

    protected virtual void OnLocationChanged(Point e)
    {
      if (DisableNotification) return;
      var handler = IndexLocationChanged;
      if (handler != null) handler(this, e);
    }

    public event EventHandler<PointF> DataLocationChanged;

    protected virtual void OnDataLocationChanged(PointF e)
    {
      if (DisableNotification) return;
      var handler = DataLocationChanged;
      if (handler != null) handler(this, e);
    }

    private void OnTimerTick(object sender, EventArgs e)
    {
      if (Model == null) return;
      var bmp = Model.ToImage();
      pictureBox1.Image = bmp;
      timer1.Enabled = false;
    }

    private void UpdateUI()
    {
      timer1.Enabled = true;
    }

    public void LoadModel(DmpModel model)
    {
      Painting = false;
      Model = model;
      UpdateUI();
    }

    private void SetValueAround(Point point)
    {
      if (DisableEditing) return;

      Parallel.For(0, PencilSize, i =>
      {
        for (var j = 0; j < PencilSize; j++)
        {
          var offSetX = Math.Floor(PencilSize/2f) - i;
          var offSetY = Math.Floor(PencilSize/2f) - j;


          var posX = (int) Math.Round(point.X - offSetX);
          var posY = (int) Math.Round(point.Y - offSetY);

          if (posX < 0) posX = 0;
          if (posY < 0) posY = 0;

          if (posX >= Model.Columns) posX = Model.Columns - 1;
          if (posY >= Model.Rows) posY = Model.Rows - 1;

          var distance = Math.Sqrt(Math.Pow(point.X - posX, 2) + Math.Pow(point.Y - posY, 2));

          if (distance > PencilSize/2f) continue;

          Model[posX, posY] = CurrentValue;
        }
        Application.DoEvents();
      });

      Model[point.X, point.Y] = CurrentValue;
      UpdateUI();
    }

    private void OnMouseClick(object sender, MouseEventArgs e)
    {
      if (Model == null) return;

      var point = GetPointForMouse(e.X, e.Y);

      if (DisableEditing) return;

      SetValueAround(point);
    }

    private Point GetPointForMouse(int x, int y)
    {
      if (Model == null) return Point.Empty;

      var stretchX = (Model.Columns - 1)/(double) pictureBox1.Width;
      var stretchY = (Model.Rows - 1)/(double) pictureBox1.Height;

      var point = new Point(
        Math.Max(Math.Min((int) Math.Round(stretchX*x + stretchX/2f), Model.Columns - 1), 0),
        Math.Max(Math.Min((int) Math.Round(stretchY*y + stretchY/2f), Model.Rows - 1), 0));

      OnDataLocationChanged(new PointF(
        (float) ((point.X/(float) Model.Columns)*(Model.MaxX - Model.MinX) + Model.MinX),
        (float) ((point.Y/(float) Model.Rows)*(Model.MaxY - Model.MinY) + Model.MinY)));

      OnLocationChanged(point);

      return point;
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
      var point = GetPointForMouse(e.X, e.Y);

      if (Model == null || !Painting) return;

      SetValueAround(point);
    }

    private void OnMouseDown(object sender, MouseEventArgs e)
    {
      Painting = e.Button == MouseButtons.Left;
    }

    private void OnMouseUp(object sender, MouseEventArgs e)
    {
      Painting = false;
    }

    private void OnMouseLeave(object sender, EventArgs e)
    {
      Painting = false;
    }
  }
}<file_sep>/LibEditSpatial/Controls/CtrlTime.cs
﻿using System;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Controls
{
  public partial class CtrlTime : UserControl
  {
    public CtrlTime()
    {
      InitializeComponent();
    }

    public TimeLoopConfig Model { get; set; }

    public void LoadModel(TimeLoopConfig model)
    {
      Model = model;
      txtTime.Text = Model.Time.ToString();
      txtInitialStep.Text = Model.InitialStep.ToString();
      txtMinStep.Text = Model.MinStep.ToString();
      txtMaxStep.Text = Model.MaxStep.ToString();
      txtPlotStep.Text = Model.PlotStep.ToString();
    }

    private void OnEndTimeChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtTime.Text, out temp))
        Model.Time = temp;
    }

    private void OnInitialStepChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtInitialStep.Text, out temp))
        Model.InitialStep = temp;
    }

    private void OnMinStepChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtMinStep.Text, out temp))
        Model.MinStep = temp;
    }

    private void OnMaxStepChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtMaxStep.Text, out temp))
        Model.MaxStep = temp;
    }

    private void OnPlotStepChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      double temp;
      if (double.TryParse(txtPlotStep.Text, out temp))
        Model.PlotStep = temp;
    }
  }
}<file_sep>/LibEditSpatial/Controls/PalleteArgs.cs
using System;

namespace LibEditSpatial.Controls
{
  public class PalleteArgs
  {
    public double Min { get; set; }

    public double Max { get; set; }
    
    public double Value { get; set; }

    public double Range
    {
      get { return Max - Min; }
    }

    public double MapToUnit(double value)
    {
      if (value < Min) return 0;
      if (value > Max) return 1;

      if (Math.Abs(Range) < 1e-10) return 0;

      return (value - Min) / Range;
    }

    public double MapFromUnit(double value)
    {
      return value * Range + Min;
    }

  }
}
<file_sep>/WFEditSpatial/Controls/ControlDomainTypes.cs
﻿using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlDomainTypes : BaseSpatialControl
  {
    public ControlDomainTypes()
    {
      InitializeComponent();
    }

    private Geometry Current { get; set; }

    public void InitializeFrom(Geometry geometry)
    {
      grid.Rows.Clear();
      Current = geometry;
      if (geometry == null) return;
      for (long i = 0; i < geometry.getNumDomainTypes(); ++i)
      {
        var domainType = geometry.getDomainType(i);
        grid.Rows.Add(domainType.getId(), domainType.getSpatialDimensions());
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;
      for (var i = 0; i < grid.Rows.Count && i < Current.getNumDomainTypes(); ++i)
      {
        var current = Current.getDomainType(i);
        var row = grid.Rows[i];
        current.setId((string) row.Cells[0].Value);
        current.setSpatialDimensions((int) row.Cells[1].Value);
      }
      OnModelChanged();
    }


    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }
  }
}<file_sep>/WFEditSpatial/Model/SpatialSpecies.cs
namespace EditSpatial.Model
{
  public class SpatialSpecies
  {
    public string Id { get; set; }
    public double MinBoundaryX { get; set; }
    public double MinBoundaryY { get; set; }
    public double MaxBoundaryX { get; set; }
    public double MaxBoundaryY { get; set; }
    public double DiffusionX { get; set; }
    public double DiffusionY { get; set; }
    public string BCType { get; set; }
    public string InitialCondition { get; set; }

    protected bool Equals(SpatialSpecies other)
    {
      return string.Equals(Id, other.Id);
    }

    public override int GetHashCode()
    {
      return (Id != null ? Id.GetHashCode() : 0);
    }

    public override string ToString()
    {
      return Id;
    }

    public override bool Equals(object obj)
    {
      if (ReferenceEquals(null, obj)) return false;
      if (ReferenceEquals(this, obj)) return true;
      if (obj is string) return Id == (string) obj;
      if (obj.GetType() != GetType()) return false;
      return Equals((SpatialSpecies) obj);
    }
  }
}<file_sep>/EditEvents.md
## Event Support
Discrete events are helpful to modify the behavior of the simulation. We added only limited support for time based events, that is events that hit after a specific time has been reached. To create events, you place a file called `eventinfo.ini`. Here an example INI file: 


	skipOutputUntilEvent = true
	
	[event0] 
	start = 10                 # start time of the event, the first accepted time point after this the event will be applied
	target = 0                 # index of the variable to assign to 
	file = event.dmp           # the dmp file with the information
	uniform = 0                # alternatively, the value to be assigned to it uniformly

You can add as many sections in that file as you need. Each section should contain the following entries: 

* `start`: this has to be a numeric value indicating the start time, when this event ought to hit. Note that no interpolation is performed here. As soon as the start time is hit (or passed) the event will be applied. 
* `target`: this is a zero based index of the model variable to be changed. To find the index for the variable to be changed you could have a look at the `host.config` file, there in the `[Data]` section all variables are listed, in the same order. (A future version will allow to use the variable names directly)
* `file`: this is a `.dmp` file containing the concentrations to be set onto the variable. In case a uniform value should be applied, this entry can be left empty. 
* `uniform`: if no `file` is specified, this value will be taken when the event is applied, and the whole field is uniformly filled. 

There is also a boolean entry `skipOutputUntilEvent` in the general section outside the file. If this value is set to `true`, output will be suppressed until the first event is applied.

---
  
<file_sep>/WFEditSpatial/Model/CreateModel.cs
﻿using System.Collections.Generic;

namespace EditSpatial.Model
{
  public class CreateModel
  {
    public CreateModel()
    {
      Species = new List<SpatialSpecies>();
      Geometry = new GeometrySettings();
    }

    public List<SpatialSpecies> Species { get; set; }
    public GeometrySettings Geometry { get; set; }

    public SpatialSpecies this[string id]
    {
      get { return Species.Find(species => species.Id == id); }
    }
  }
}<file_sep>/WFEditSpatial/Model/SpatialModel.cs
﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using EditSpatial.Converter;
using libsbmlcs;
using libSbl2SBML;
using SBMLSupport;

namespace EditSpatial.Model
{
  public class SpatialModel
  {
    public bool Dirty { get; set; }


    internal static CustomSpatialValidator CustomSpatialValidator = new CustomSpatialValidator();

    public SpatialModel()
    {
      DefaultHeight = DefaultWidth = 50;
      FileName = "untitled.xml";
    }

    public double DefaultWidth { get; set; }
    public double DefaultHeight { get; set; }

    public double Width
    {
      get
      {
        if (!IsSpatial || Geometry == null) return DefaultWidth;

        return GetBoundaryMaxValue(0, DefaultWidth);
      }
    }

    public double Height
    {
      get
      {
        if (!IsSpatial || Geometry == null) return DefaultHeight;

        return GetBoundaryMaxValue(1, DefaultHeight);
      }
    }

    public SBMLDocument Document { get; set; }

    public Geometry Geometry
    {
      get
      {
        if (!IsSpatial) return null;

        var plugin = (SpatialModelPlugin)Document.getModel().getPlugin("spatial");
        if (plugin == null) return null;
        return plugin.getGeometry();
      }
    }

    public string FileName { get; set; }

    public bool IsSpatial
    {
      get
      {
        if (Document == null) return false;
        if (Document.getPlugin("spatial") == null) return false;
        if (Document.getModel() == null) return false;
        if (Document.getModel().getPlugin("spatial") == null) return false;
        return true;
      }
    }

    public bool HaveModel
    {
      get { return Document != null && Document.getModel() != null; }
    }

    public void ExportDuneSBML(string filename)
    {
      try
      {
        var converter = new DuneConverter(Document);
        File.WriteAllText(filename, converter.ToSBML());
      }
      catch
      {
      }
    }

    private double GetBoundaryMaxValue(int val, double defaultValue = 0)
    {
      if (val >= Geometry.getNumCoordinateComponents()) return defaultValue;
      try
      {
        return Geometry.getCoordinateComponent(val).getBoundaryMax().getValue();
      }
      catch
      {
        return defaultValue;
      }
    }

    public static SpatialModel FromFile(string fileName)
    {
      var model = new SpatialModel
      {
        Document = libsbml.readSBMLFromFile(fileName),
        FileName = fileName
      };

      if (model.Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) > 0)
      {
        var docString = File.ReadAllText(fileName);
        if (docString.Contains("spatial:spatialId"))
        {
          var assembly = System.Reflection.Assembly.GetAssembly(typeof(SBMLDocument));
          var converter = from type in assembly.GetTypes() where type.Name == "OldSpatialToSpatialConverter" select type;
          if (converter != null && converter.Any())
            model.Document = converter.First().GetMethod("convertOldSpatialSBMLFromString").Invoke(null, new object[] { docString }) as SBMLDocument;
          //libsbml.Oldspat
        }
      }

      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, false);
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_MODELING_PRACTICE, false);
      model.Document.addValidator(CustomSpatialValidator);
      model.Document.validateSBML();
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, false);
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_MODELING_PRACTICE, false);
      return model;
    }

    public static SpatialModel FromJarnac(string content)
    {
      NOM.Namespaces = new List<string>();
      var model = TModel.ReadModel(content);
      model.RemoveAnnotation();
      model.RemoveNotes();
      return FromString(model.toSBML(false));
    }

    public static SpatialModel FromString(string content)
    {
      var model = new SpatialModel
      {
        Document = libsbml.readSBMLFromString(content),
        FileName = "fromstring.xml"
      };
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, false);
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_MODELING_PRACTICE, false);
      model.Document.addValidator(CustomSpatialValidator);
      model.Document.validateSBML();
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, false);
      model.Document.setConsistencyChecks(libsbml.LIBSBML_CAT_MODELING_PRACTICE, false);
      return model;
    }

    public void SaveTo(string filename)
    {
      if (Document == null) return;
      FileName = filename;
      Dirty = false;
      libsbml.writeSBMLToFile(Document, filename);
    }

    public string ToSBML()
    {
      if (Document == null) return "<none>";
      return libsbml.writeSBMLToString(Document).Replace("\n", Environment.NewLine);
    }

    public string ToJarnac()
    {
      if (Document == null) return "";
      try
      {
        return convertSBML.ToSBL(ToSBML());
      }
      catch
      {
        return "";
      }
    }

    public void MoveAllRulesToAssignments()
    {
      if (Document == null || Document.getModel() == null)
        return;

      var model = Document.getModel();
      for (var i = (int)model.getNumRules() - 1; i >= 0; i--)
      {
        var current = model.getRule(i) as AssignmentRule;
        if (current == null) continue;
        model.MoveRuleToAssignment(current.getVariable());
      }

      OnModelChanged();
    }

    public bool FixCommonErrors()
    {
      var result = false;

      if (Document == null) return result;

      FixUnits();

      FixDiffusionCoefficients();

      if (Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) == 0) return result;

      for (var i = 0; i < Document.getNumErrors(); ++i)
      {
        var current = Document.getError(i);
        var id = current.getErrorId();
        var message = current.getMessage();

        switch (current.getErrorId())
        {
          case 99109:
            {
              var plugin = Document.getPlugin("req");
              if (plugin == null) break;
              Document.setPackageRequired("req", false);
              result = true;
              break;
            }
          case 20517:
            {
              for (var j = 0; j < Document.getModel().getNumCompartments(); ++j)
              {
                var comp = Document.getModel().getCompartment(j);
                if (!comp.isSetConstant())
                  comp.setConstant(true);
              }
              result = true;
              break;
            }
          case 20623:
            {
              for (var j = 0; j < Document.getModel().getNumSpecies(); ++j)
              {
                var species = Document.getModel().getSpecies(j);
                if (!species.isSetConstant())
                  species.setConstant(false);
              }
              result = true;
              break;
            }

          case 21116:
            {
              for (var j = 0; j < Document.getModel().getNumReactions(); ++j)
              {
                var reaction = Document.getModel().getReaction(j);
                for (var k = 0; k < reaction.getNumProducts(); ++k)
                {
                  var reference = reaction.getProduct(k);
                  if (!reference.isSetConstant())
                    reference.setConstant(true);
                }
                for (var k = 0; k < reaction.getNumReactants(); ++k)
                {
                  var reference = reaction.getReactant(k);
                  if (!reference.isSetConstant())
                    reference.setConstant(true);
                }
              }
              result = true;
              break;
            }

          case 21110:
            {
              for (var j = 0; j < Document.getModel().getNumReactions(); ++j)
              {
                var reaction = Document.getModel().getReaction(j);
                if (!reaction.isSetFast())
                  reaction.setFast(false);
                if (!reaction.isSetReversible())
                  reaction.setReversible(false);
              }
              result = true;
              break;
            }

          case 20706:
            {
              for (var j = 0; j < Document.getModel().getNumParameters(); ++j)
              {
                var parameter = Document.getModel().getParameter(j);
                if (!parameter.isSetConstant())
                  parameter.setConstant(false);
              }
              result = true;
              break;
            }

          case 20610:
            {
              // have both rule and reaction on species this error is there in old versions of 
              // vcell code, that would export initial assignments as assignment rules
              var match = Regex.Match(message.Split('\n')[2],
                ".*'(?<speciesId>\\w+?)'.*'(?<reactionId>\\w+?)'.*",
                RegexOptions.ExplicitCapture);
              if (match.Success)
              {
                var speciesId = match.Groups["speciesId"].Value;

                Document.getModel().MoveRuleToAssignment(speciesId);
                result = true;
              }
              break;
            }
          case 21121:
            {
              var match = Regex.Match(message, ".*'(?<speciesId>\\w+?)'.*'(?<reactionId>\\w+?)'.*",
                RegexOptions.ExplicitCapture);
              if (match.Success)
              {
                var speciesId = match.Groups["speciesId"].Value;
                var reactionId = match.Groups["reactionId"].Value;
                var reaction = Document.getModel().getReaction(reactionId);
                if (reaction != null)
                {
                  if (reaction.getModifier(speciesId) == null)
                  {
                    var modifier = reaction.createModifier();
                    modifier.setSpecies(speciesId);
                    result = true;
                  }
                }
              }

              break;
            }


          case 99303:
            {
              // remove the unitdefinition referenced by an element
              var match = Regex.Match(message, ".*<(?<type>\\w+?)>.*'(?<id>\\w+?)'.*",
                RegexOptions.ExplicitCapture);
              if (match.Success)
              {
                var type = match.Groups["type"].Value;
                var elementId = match.Groups["id"].Value;

                switch (type)
                {
                  case "parameter":
                    {
                      var param = Document.getModel().getParameter(elementId);
                      if (param != null)
                      {
                        param.unsetUnits();
                        result = true;
                      }
                      break;
                    }
                  default:
                    {
                      Debug.WriteLine("Need to fix type '{0}' und id '{1}'", type, elementId);
                      break;
                    }
                }
              }

              break;
            }

          default:
            Debug.WriteLine("Don't know what to do with:  {0}: {1}", id, message);
            break;
        }
      }
      Document.getErrorLog().clearLog();

      if (result)
        OnModelChanged();

      return result;
    }

    public void FixUnits()
    {
      if (Document == null) return;


      var model = Document.getModel();
      if (model == null) return;

      var needUpdate = false;

      if (!model.isSetLengthUnits())
      {
        model.setLengthUnits("length");
        needUpdate = true;
      }


      if (!model.isSetVolumeUnits())
      {
        model.setVolumeUnits("volume");
        needUpdate = true;
      }

      if (!model.isSetSubstanceUnits())
      {
        model.setSubstanceUnits("substance");
        needUpdate = true;
      }

      if (!model.isSetTimeUnits())
      {
        model.setTimeUnits("time");
        needUpdate = true;
      }


      var unit = model.getUnitDefinition("length");
      if (unit == null)
      {
        unit = model.createUnitDefinition();
        unit.setId("length");
        var kind = unit.createUnit();
        kind.setKind(libsbml.UNIT_KIND_METRE);
        kind.setExponent(1);
        kind.setScale(0);
        kind.setMultiplier(1e-06);

        needUpdate = true;
      }

      unit = model.getUnitDefinition("volume");
      if (unit == null)
      {
        unit = model.createUnitDefinition();
        unit.setId("volume");
        var kind = unit.createUnit();
        kind.setKind(libsbml.UNIT_KIND_LITRE);
        kind.setExponent(1);
        kind.setScale(-3);
        kind.setMultiplier(1);

        model.setVolumeUnits("volume");

        needUpdate = true;
      }

      unit = model.getUnitDefinition("substance");
      if (unit == null)
      {
        unit = model.createUnitDefinition();
        unit.setId("substance");
        var kind = unit.createUnit();
        kind.setKind(libsbml.UNIT_KIND_MOLE);
        kind.setExponent(1);
        kind.setScale(-6);
        kind.setMultiplier(1);

        model.setSubstanceUnits("substance");

        needUpdate = true;
      }

      unit = model.getUnitDefinition("time");
      if (unit == null)
      {
        unit = model.createUnitDefinition();
        unit.setId("time");
        var kind = unit.createUnit();
        kind.setKind(libsbml.UNIT_KIND_SECOND);
        kind.setExponent(1);
        kind.setScale(0);
        kind.setMultiplier(60);

        model.setTimeUnits("time");

        needUpdate = true;
      }

      if (needUpdate)
      OnModelChanged();


    }

    public void FixDiffusionCoefficients()
    {
      if (Document == null || !IsSpatial || Geometry == null) return;

      var numCooords = Geometry.getNumCoordinateComponents();

      var dict = new Dictionary<string, List<Tuple<int, Parameter>>>();

      for (var i = 0; i < Document.getModel().getNumParameters(); ++i)
      {
        var current = Document.getModel().getParameter(i);
        if (current == null) continue;
        var plug = current.getPlugin("spatial") as SpatialParameterPlugin;
        if (plug == null) continue;
        if (plug.getType() != libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT) continue;
        var diff = plug.getDiffusionCoefficient();
        if (diff == null || !diff.isSetCoordinateReference1()) continue;
        if (!dict.ContainsKey(diff.getVariable()))
          dict[diff.getVariable()] = new List<Tuple<int, Parameter>>();
        dict[diff.getVariable()].Add(new Tuple<int, Parameter>((int)diff.getCoordinateReference1(), current));
      }

      foreach (var key in dict.Keys)
      {
        var current = dict[key];
        if (current == null || current.Count == 0 || current.Count == numCooords) continue;

        foreach (var entry in current)
        {
          var param = entry.Item2;
          if (param == null) continue;
          var plug = param.getPlugin("spatial") as SpatialParameterPlugin;
          if (plug == null) continue;
          if (plug.getType() != libsbml.SBML_SPATIAL_DIFFUSIONCOEFFICIENT) continue;
          var diff = plug.getDiffusionCoefficient();
          if (diff == null || !diff.isSetCoordinateReference1()) continue;
          diff.unsetCoordinateReference1();
        }

        //for (int i = 0; i < 3; ++i)
        //{
        //  var param = current.FirstOrDefault(e => e.Item1 == i);
        //  if (param != null) continue;
        //}
      }
    }

    public bool ConvertToL3()
    {
      if (Document.getLevel() < 3)
      {
        var prop = new ConversionProperties(new SBMLNamespaces(3, 1));
        prop.addOption("strict", false);
        prop.addOption("setLevelAndVersion", true);
        prop.addOption("ignorePackages", true);

        if (Document.convert(prop) != libsbml.LIBSBML_OPERATION_SUCCESS)
        {
          return false;
        }
      }

      Document.enablePackage(SpatialExtension.getXmlnsL3V1V1(), "spatial", true);
      Document.setPackageRequired("spatial", true);

      //Document.enablePackage(RequiredElementsExtension.getXmlnsL3V1V1(), "req", true);
      //Document.setPackageRequired("req", false);

      return true;
    }

    public bool ConvertToSpatial(CreateModel createModel)
    {
      if (Document == null) return true;

      if (!ConvertToL3()) return false;

      var model = Document.getModel();
      var plugin = (SpatialModelPlugin)model.getPlugin("spatial");
      if (plugin == null)
        return false;

      var geometry = plugin.getGeometry();
      if (geometry == null)
        geometry = plugin.createGeometry();

      if (geometry == null)
        return false;

      // create coordinate components    
      CreateCoordinateSystem(geometry, model, createModel.Geometry);

      //if (geometry.getNumGeometryDefinitions()==0)
      if (!SetupGeometry(model, geometry, createModel)) return false;

      SetupSpecies(createModel, model);

      SetIsLocalOnReactions(model);

      return true;
    }

    private void SplitReaction(libsbmlcs.Model model, Reaction reaction)
    {
      var info = new ReactionInfo(reaction);
      if (info.CompartmentIds.Count < 2) return;

      if (info.CompartmentIds.Count == 2)
      {
        // straight transport
        var comp1Id = info.CompartmentIds[0];
        var comp1 = model.getCompartment(comp1Id);
        if (comp1 == null || comp1.getSpatialDimensions() == 2) return;
        var comp2Id = info.CompartmentIds[1];
        var comp2 = model.getCompartment(comp2Id);
        if (comp2 == null || comp2.getSpatialDimensions() == 2) return;

        var memId = String.Format("mem_{0}_{1}", comp2Id, comp1Id);
        var memComp = model.getCompartment(memId);
        if (memComp == null)
        {
          memId = String.Format("mem_{0}_{1}", comp1Id, comp2Id);
          memComp = model.getCompartment(memId);
        }
        if (memComp == null)
        {
          memComp = model.createCompartment();
          memComp.initDefaults();
          memComp.setId(memId);
          memComp.setSize(1);
          memComp.setUnits("area");
          memComp.setSpatialDimensions(2);
        }

        var math = new ASTNode(reaction.getKineticLaw().getMath());
        var react1 = model.createReaction();
        react1.initDefaults();
        react1.setReversible(reaction.getReversible());
        react1.setId(String.Format("mem_{0}_{1}", comp1Id, reaction.getId()));

        var react2builder = new StringBuilder("1");
        var react2 = model.createReaction();
        react2.initDefaults();
        react2.setReversible(reaction.getReversible());
        react2.setId(String.Format("mem_{0}_{1}", comp2Id, reaction.getId()));


        for (var i = 0; i < reaction.getNumReactants(); ++i)
        {
          var reference = reaction.getReactant(i);
          var species = model.getSpecies(reference.getSpecies());
          if (species.getCompartment() == comp1Id)
          {
            var newRef = react1.createReactant();
            newRef.setSpecies(reference.getSpecies());
            newRef.setStoichiometry(reference.getStoichiometry());
          }
          else
          {
            var newId = "mem_" + species.getId();
            var newSpecies = model.getSpecies(newId);
            if (newSpecies == null)
            {
              newSpecies = model.createSpecies();
              newSpecies.initDefaults();
              newSpecies.setId(newId);
              newSpecies.setName(species.getId());
              newSpecies.setCompartment(memId);
              newSpecies.setInitialConcentration(0);
            }

            var newRef = react2.createProduct();
            newRef.setSpecies(newSpecies.getId());
            newRef.setStoichiometry(reference.getStoichiometry());

            newRef = react1.createReactant();
            newRef.setSpecies(newSpecies.getId());
            newRef.setStoichiometry(reference.getStoichiometry());

            react2builder.Append(" * " + newSpecies.getId());
            math.renameSIdRefs(species.getId(), newSpecies.getId());
          }
        }


        for (var i = 0; i < reaction.getNumProducts(); ++i)
        {
          var reference = reaction.getProduct(i);
          var species = model.getSpecies(reference.getSpecies());
          if (species.getCompartment() == comp1Id)
          {
            var newRef = react1.createProduct();
            newRef.setSpecies(species.getId());
            newRef.setStoichiometry(reference.getStoichiometry());
          }
          else
          {
            var newId = "mem_" + species.getId();
            var newSpecies = model.getSpecies(newId);
            if (newSpecies == null)
            {
              newSpecies = model.createSpecies();
              newSpecies.initDefaults();
              newSpecies.setId(newId);
              newSpecies.setName(species.getId());
              newSpecies.setCompartment(memId);
              newSpecies.setInitialConcentration(0);
            }

            var newRef = react1.createProduct();
            newRef.setSpecies(newSpecies.getId());
            newRef.setStoichiometry(reference.getStoichiometry());

            newRef = react2.createReactant();
            newRef.setSpecies(newSpecies.getId());
            newRef.setStoichiometry(reference.getStoichiometry());
            react2builder.Append(" * " + newSpecies.getId());

            newRef = react2.createProduct();
            newRef.setSpecies(species.getId());
            newRef.setStoichiometry(reference.getStoichiometry());
            math.renameSIdRefs(species.getId(), newSpecies.getId());
          }
        }

        var law = react2.createKineticLaw();
        law.setFormula(react2builder.ToString());

        law = react1.createKineticLaw();
        for (var l = 0; l < reaction.getKineticLaw().getNumLocalParameters(); ++l)
        {
          var param = reaction.getKineticLaw().getParameter(l);
          var newParam = law.createParameter();
          newParam.initDefaults();
          newParam.setId(param.getId());
          newParam.setValue(param.getValue());
        }
        law.setMath(math);

        model.removeReaction(reaction.getId());
        reaction.Dispose();
      }
    }

    /// <summary>
    ///   This method first goes ahead, and looks through reactions,
    ///   whether a transport reaction can be found. If so, it tests
    ///   whether the compartments differ in spatialDimensions (i.e
    ///   checks that the transport goes via a membrane), if it does
    ///   not, it will create a membrane compartment, and split the
    ///   reaction such that it will go from the compartment -&gt; to
    ///   the membrane -&gt; to the other compartment.
    /// </summary>
    /// <param name="model">the model to check</param>
    internal void CorrectCompartmentsAndTransport(libsbmlcs.Model model)
    {
      for (var i = (int)model.getNumReactions() - 1; i >= 0; --i)
      {
        var reaction = model.getReaction(i);
        var comps = Util.GetCompartmentsFromReaction(reaction);
        if (comps.Count < 2)
          continue;

        SplitReaction(model, reaction);
      }
    }

    private bool SetupGeometry(libsbmlcs.Model model, Geometry geometry,
      CreateModel createModel)
    {
      var numCompartments = model.getNumCompartments();
      if (numCompartments == 1 || MainForm.Settings.IgnoreMultiCompartments)
        return SetupUnicompartmentalGeometry(model, geometry, createModel);

      var num3DComps = 0;
      for (var index = 0; index < numCompartments; ++index)
      {
        var current = model.getCompartment(index);
        if (current == null || !current.isSetSpatialDimensions()) continue;
        if (current.getSpatialDimensions() == 3) num3DComps++;
      }

      var length = createModel.Geometry.Xmax / num3DComps;
      var def = geometry.createAnalyticGeometry();
      def.setId("geometry");

      var order = OrderCompartments(model);
      var i = 0;
      AdjacentDomains lastAdjacent = null;
      Domain memDomain = null;
      for (var j = 0; j < order.Count; j++)
      {
        var comp = model.getCompartment(order[j]);
        var cplug = (SpatialCompartmentPlugin)comp.getPlugin("spatial");
        if (cplug == null)
          return false;
        var domainType = geometry.createDomainType();
        domainType.setId("domainType_" + order[j]);
        domainType.setSpatialDimensions((int)comp.getSpatialDimensionsAsDouble());
        var domain = geometry.createDomain();
        domain.setId("domain_" + order[j]);
        domain.setDomainType("domainType_" + order[j]);
        var point = domain.createInteriorPoint();
        point.setCoord1(i * length + length / 2.0);
        point.setCoord2(createModel.Geometry.Ymax / 2.0);
        point.setCoord3(0);
        if (lastAdjacent != null)
        {
          var adj = geometry.createAdjacentDomains();
          adj.setId(String.Format("adj_{0}_{1}", memDomain.getId(), domain.getId()));
          adj.setDomain1(memDomain.getId());
          adj.setDomain2(domain.getId());
        }
        var vol = def.createAnalyticVolume();
        vol.setId("vol_" + order[j]);
        vol.setDomainType("domainType_" + order[j]);
        vol.setFunctionType(libsbml.SPATIAL_FUNCTIONKIND_LAYERED);
        vol.setOrdinal(i);
        vol.setMath(libsbml.parseFormula(i == 0
          ? "1" // first compartment gets all
          : j == order.Count - 1
            ? string.Format("geq(x, {0})", (i * length)) // last compartment gets rest
            : string.Format("and(geq(x, {0}), lt(x, {1}))", (i * length), ((i + 1) * length))));
        var map = cplug.getCompartmentMapping();
        if (map == null)
          map = cplug.createCompartmentMapping();

        map.setId("mapping_" + order[j]);
        map.setDomainType(domainType.getId());
        map.setUnitSize(1);

        if (i + 1 < numCompartments)
        {
          if (j + 1 < order.Count && model.getCompartment(order[j + 1]).getSpatialDimensions() == 2)
          {
            // next compartment is a 2D compartment!
            domainType = geometry.createDomainType();
            domainType.setId("domainType_" + order[j + 1]);
            domainType.setSpatialDimensions(2);

            comp = model.getCompartment(order[j + 1]);
            cplug = (SpatialCompartmentPlugin)comp.getPlugin("spatial");
            if (cplug == null)
              return false;
            map = cplug.getCompartmentMapping();
            if (map == null)
              map = cplug.createCompartmentMapping();

            map.setId("mapping_" + comp.getId());
            map.setDomainType(domainType.getId());
            map.setUnitSize(1);

            memDomain = geometry.createDomain();
            memDomain.setId("domain_mem_" + order[j]);
            memDomain.setDomainType(domainType.getId());
            lastAdjacent = geometry.createAdjacentDomains();
            lastAdjacent.setId(String.Format("adj_{0}_{1}", memDomain.getId(), domain.getId()));
            lastAdjacent.setDomain1(memDomain.getId());
            lastAdjacent.setDomain2(domain.getId());

            ++j; //++i;

            //vol = def.createAnalyticVolume();
            //vol.setId("vol_" + order[j]);
            //vol.setDomainType("domainType_" + order[j]);
            //vol.setFunctionType(libsbml.SPATIAL_FUNCTIONKIND_LAYERED);
            //vol.setOrdinal(i);
            //vol.setMath(libsbml.parseFormula(i == 0 ? "1" : //            string.Format(
            //  //"and(geq(x, {0}), lt(x, {1}))", (i * length), ((i + 1) * length))
            //string.Format(//"and(and(geq(x, {0}), lt(x, {1})), and(geq(y, {0}), lt(y, {1})))", (i * length), ((i + 1) * length))
            //"and(geq(x, {0}), lt(x, {1}))", (i * length), ((i + 1) * length))));
          }
          else
          {
            domainType = geometry.createDomainType();
            domainType.setId("mem_" + order[j]);
            domainType.setSpatialDimensions(2);
            comp = model.createCompartment();
            comp.initDefaults();
            comp.setId("c_mem_" + order[j]);
            comp.setSize(1);
            cplug = (SpatialCompartmentPlugin)comp.getPlugin("spatial");
            if (cplug == null)
              return false;
            map = cplug.getCompartmentMapping();
            if (map == null)
              map = cplug.createCompartmentMapping();

            map.setId("mapping_" + comp.getId());
            map.setDomainType(domainType.getId());
            map.setUnitSize(1);
            memDomain = geometry.createDomain();
            memDomain.setId("domain_mem_" + order[j]);
            memDomain.setDomainType("mem_" + order[j]);
            lastAdjacent = geometry.createAdjacentDomains();
            lastAdjacent.setId(String.Format("adj_{0}_{1}", memDomain.getId(), domain.getId()));
            lastAdjacent.setDomain1(memDomain.getId());
            lastAdjacent.setDomain2(domain.getId());
          }
        }
        ++i;
      }
      return true;
    }

    private static bool SetupUnicompartmentalGeometry(libsbmlcs.Model model, Geometry geometry, CreateModel createModel)
    {
      var domainType = geometry.createDomainType();
      domainType.setId("domainType0");
      domainType.setSpatialDimensions(3);

      var domain = geometry.createDomain();
      domain.setId("domain0");
      domain.setDomainType("domainType0");
      var point = domain.createInteriorPoint();
      point.setCoord1(createModel.Geometry.Xmax / 2.0);
      point.setCoord2(createModel.Geometry.Ymax / 2.0);
      point.setCoord3(0);


      AnalyticVolume vol = null;
      AnalyticGeometry def = null;

      def = geometry.GetFirstAnalyticGeometry();
      if (def == null)
      {
        def = geometry.createAnalyticGeometry();
        def.setId("geometry0");
      }

      vol = def.getAnalyticVolume(0);
      if (vol == null)
      {
        vol = def.createAnalyticVolume();
        vol.setId("vol0");
        vol.setOrdinal(0);
      }

      if (!vol.isSetMath() || string.IsNullOrWhiteSpace(libsbml.formulaToL3String(vol.getMath())))
        vol.setMath(libsbml.parseL3Formula("1"));

      vol.setDomainType("domainType0");
      vol.setFunctionType(libsbml.SPATIAL_FUNCTIONKIND_LAYERED);


      var comp = model.getCompartment(0);
      var cplug = (SpatialCompartmentPlugin)comp.getPlugin("spatial");
      if (cplug == null)
        return false;

      var map = cplug.getCompartmentMapping();
      if (map == null)
        map = cplug.createCompartmentMapping();

      map.setId("mapping0");
      map.setDomainType(domainType.getId());
      map.setUnitSize(1);

      if (createModel.Geometry.WrapOutside)
      {
        comp = model.createCompartment();
        comp.initDefaults();
        comp.setId("__outside");
        cplug = (SpatialCompartmentPlugin)comp.getPlugin("spatial");

        domainType = geometry.createDomainType();
        domainType.setId("domainType1");
        domainType.setSpatialDimensions(3);

        domain = geometry.createDomain();
        domain.setId("domain1");
        domain.setDomainType("domainType1");
        point = domain.createInteriorPoint();
        point.setCoord1(0);
        point.setCoord2(0);
        point.setCoord3(0);

        map = cplug.getCompartmentMapping();
        if (map == null)
          map = cplug.createCompartmentMapping();

        map.setId("mapping1");
        map.setDomainType(domainType.getId());
        map.setUnitSize(1);

        vol.setOrdinal(1);
        vol.setMath(
          libsbml.parseFormula(
            string.Format("piecewise(1, and(geq(x, {0}), leq(x, {1}), geq(y, {2}), leq(y, {3})), 0)",
              createModel.Geometry.Xmin + 1, createModel.Geometry.Xmax - 1,
              createModel.Geometry.Ymin + 1, createModel.Geometry.Ymax - 1
              )
            ));

        vol = def.createAnalyticVolume();
        vol.setId("vol1");
        vol.setDomainType("domainType1");
        vol.setFunctionType(libsbml.SPATIAL_FUNCTIONKIND_LAYERED);
        vol.setOrdinal(0);
        vol.setMath(libsbml.parseFormula("1"));

        // membrane
        comp = model.createCompartment();
        comp.initDefaults();
        comp.setId("__membrane");
        cplug = (SpatialCompartmentPlugin)comp.getPlugin("spatial");

        domainType = geometry.createDomainType();
        domainType.setId("domainType2");
        domainType.setSpatialDimensions(2);

        domain = geometry.createDomain();
        domain.setId("domain2");
        domain.setDomainType("domainType2");

        map = cplug.getCompartmentMapping();
        if (map == null)
          map = cplug.createCompartmentMapping();

        map.setId("mapping2");
        map.setDomainType(domainType.getId());
        map.setUnitSize(1);

        var adjacent = geometry.createAdjacentDomains();
        adjacent.setId("adj_1");
        adjacent.setDomain1("domain2");
        adjacent.setDomain2("domain1");

        adjacent = geometry.createAdjacentDomains();
        adjacent.setId("adj_2");
        adjacent.setDomain1("domain2");
        adjacent.setDomain2("domain0");
      }

      return true;
    }

    private static void SetupSpecies(CreateModel createModel, libsbmlcs.Model model)
    {
      var spatialElements = (from s in createModel.Species select s.Id).ToList();

      for (var i = 0; i < model.getNumSpecies(); i++)
      {
        var species = model.getSpecies(i);
        var splug = (SpatialSpeciesPlugin)species.getPlugin("spatial");
        if (splug == null) continue;
        splug.setIsSpatial(false);
        var id = species.getId();
        if (!spatialElements.Contains(id)) continue;

        var currentSpecies = createModel[id];
        species.setInitialExpession(currentSpecies.InitialCondition);
        splug.setIsSpatial(true);
        SetRequiredElements(species);

        var temp = species.getParameterDiffusionX();
        if (temp != null && temp.isSetId())
          model.removeParameter(temp.getId());
        var param = model.createParameter();
        param.initDefaults();
        param.setId(id + "_diff_X");
        param.setValue(currentSpecies.DiffusionX);
        var pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        var diff = pplug.getDiffusionCoefficient();
        if (diff == null)
          diff = pplug.createDiffusionCoefficient();
        diff.setVariable(id);
        diff.setType(libsbml.SPATIAL_DIFFUSIONKIND_ANISOTROPIC);
        diff.setCoordinateReference1(0);
        SetRequiredElements(param);

        temp = species.getParameterDiffusionY();
        if (temp != null && temp.isSetId())
          model.removeParameter(temp.getId());
        model.removeParameter(id + "_diff_Y");
        param = model.createParameter();
        param.initDefaults();
        param.setId(id + "_diff_Y");
        param.setValue(currentSpecies.DiffusionY);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        diff = pplug.getDiffusionCoefficient();
        if (diff == null)
          diff = pplug.createDiffusionCoefficient();
        diff.setVariable(id);
        diff.setType(libsbml.SPATIAL_DIFFUSIONKIND_ANISOTROPIC);
        diff.setCoordinateReference1(1);
        SetRequiredElements(param);

        temp = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Xmin");
        if (temp != null && temp.isSetId())
          model.removeParameter(temp.getId());
        param = model.createParameter();
        param.initDefaults();
        param.setId(id + "_BC_Xmin");
        param.setValue(currentSpecies.MinBoundaryX);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        var bc = pplug.getBoundaryCondition();
        if (bc == null)
          bc = pplug.createBoundaryCondition();
        bc.setVariable(id);
        bc.setCoordinateBoundary("Xmin");
        bc.setType(currentSpecies.BCType == "Dirichlet" ?  libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET :  libsbml.SPATIAL_BOUNDARYKIND_NEUMANN);
        SetRequiredElements(param);

        temp = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Xmax");
        if (temp != null && temp.isSetId())
          model.removeParameter(temp.getId());
        param = model.createParameter();
        param.initDefaults();
        param.setId(id + "_BC_Xmax");
        param.setValue(currentSpecies.MaxBoundaryX);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        bc = pplug.getBoundaryCondition();
        if (bc == null)
          bc = pplug.createBoundaryCondition();
        bc.setVariable(id);
        bc.setCoordinateBoundary("Xmax");
        bc.setType(currentSpecies.BCType == "Dirichlet" ? libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET : libsbml.SPATIAL_BOUNDARYKIND_NEUMANN);
        SetRequiredElements(param);

        temp = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Ymin");
        if (temp != null && temp.isSetId())
          model.removeParameter(temp.getId());

        param = model.createParameter();
        param.initDefaults();
        param.setId(id + "_BC_Ymin");
        param.setValue(currentSpecies.MinBoundaryY);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        bc = pplug.getBoundaryCondition();
        if (bc == null)
          bc = pplug.createBoundaryCondition();
        bc.setVariable(id);
        bc.setCoordinateBoundary("Ymin");
        bc.setType(currentSpecies.BCType == "Dirichlet" ? libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET : libsbml.SPATIAL_BOUNDARYKIND_NEUMANN);
        SetRequiredElements(param);

        temp = species.getSpatialParameter(libsbml.SBML_SPATIAL_BOUNDARYCONDITION, "Ymax");
        if (temp != null && temp.isSetId())
          model.removeParameter(temp.getId());

        param = model.createParameter();
        param.initDefaults();
        param.setId(id + "_BC_Ymax");
        param.setValue(currentSpecies.MaxBoundaryY);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        bc = pplug.getBoundaryCondition();
        if (bc == null)
          bc = pplug.createBoundaryCondition();
        bc.setVariable(id);
        bc.setCoordinateBoundary("Ymax");
        bc.setType(currentSpecies.BCType == "Dirichlet" ? libsbml.SPATIAL_BOUNDARYKIND_DIRICHLET : libsbml.SPATIAL_BOUNDARYKIND_NEUMANN);
        SetRequiredElements(param);
      }
    }

    private void SetIsLocalOnReactions(libsbmlcs.Model model)
    {
      for (var i = 0; i < model.getNumReactions(); i++)
      {
        var reaction = model.getReaction(i);
        var rplug = (SpatialReactionPlugin)reaction.getPlugin("spatial");
        if (rplug == null) continue;
        SetRequiredElements(reaction);
        var idsContainedIn = GetSpeciesReferenceIdsContainedIn(reaction);
        var isLocal = idsContainedIn.Count > 0;
        rplug.setIsLocal(isLocal);
      }
    }

    public void CreateCoordinateSystem(Geometry geometry, libsbmlcs.Model model,
      GeometrySettings settings)
    {
      geometry.setCoordinateSystem(libsbml.SPATIAL_GEOMETRYKIND_CARTESIAN);

      var coord = geometry.getCoordinateComponent("x");
      if (coord == null)
      {
        coord = geometry.createCoordinateComponent();
      }

      var unitDef = model.getUnitDefinition("um");
      if (unitDef == null)
      {
        unitDef = model.createUnitDefinition();
        unitDef.setId("um");
        var kind = unitDef.createUnit();
        kind.setExponent(1);
        kind.setMultiplier(1e-6);
        kind.setScale(0);
        kind.setKind(libsbml.UNIT_KIND_METRE);
      }


      coord.setId("x");
      coord.setUnit("um");
      coord.setType(libsbml.SPATIAL_COORDINATEKIND_CARTESIAN_X);

      var min = coord.getBoundaryMin();
      if (min == null)
        min = coord.createBoundaryMin();
      min.setId("Xmin");
      min.setValue(settings.Xmin);
      var max = coord.getBoundaryMax();
      if (max == null) max = coord.createBoundaryMax();
      max.setId("Xmax");
      max.setValue(settings.Xmax);

      coord = geometry.getCoordinateComponent("y");
      if (coord == null)
        coord = geometry.createCoordinateComponent();
      coord.setId("y");
      coord.setUnit("um");
      coord.setType(libsbml.SPATIAL_COORDINATEKIND_CARTESIAN_Y);
      min = coord.getBoundaryMin();
      if (min == null)
        min = coord.createBoundaryMin();
      min.setId("Ymin");
      min.setValue(settings.Ymin);
      max = coord.getBoundaryMax();
      if (max == null)
        max = coord.createBoundaryMax();
      max.setId("Ymax");
      max.setValue(settings.Ymax);

      model.removeParameter("x");
      var param = model.createParameter();
      param.initDefaults();
      param.setId("x");
      param.setValue(0);
      var pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
      var symbol = pplug.createSpatialSymbolReference();
      symbol.setSpatialRef("x");
      SetRequiredElements(param, false);

      model.removeParameter("y");
      param = model.createParameter();
      param.initDefaults();
      param.setId("y");
      param.setValue(0);
      pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
      symbol = pplug.createSpatialSymbolReference();
      symbol.setSpatialRef("y");
      SetRequiredElements(param, false);

      if (settings.UsedSymbols.Contains("width"))
      {
        model.removeParameter("width");
        param = model.createParameter();
        param.initDefaults();
        param.setId("width");
        param.setValue(settings.Xmax);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        symbol = pplug.createSpatialSymbolReference();
        symbol.setSpatialRef("Xmax");
        SetRequiredElements(param, false);
      }

      if (settings.UsedSymbols.Contains("height"))
      {
        model.removeParameter("height");
        param = model.createParameter();
        param.initDefaults();
        param.setId("height");
        param.setValue(settings.Ymax);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        symbol = pplug.createSpatialSymbolReference();
        symbol.setSpatialRef("Ymax");
        SetRequiredElements(param, false);
      }

      if (settings.UsedSymbols.Contains("Xmax"))
      {
        model.removeParameter("Xmax");
        param = model.createParameter();
        param.initDefaults();
        param.setId("Xmax");
        param.setValue(settings.Xmax);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        symbol = pplug.createSpatialSymbolReference();
        symbol.setSpatialRef("Xmax");
        SetRequiredElements(param, false);
      }

      if (settings.UsedSymbols.Contains("Ymax"))
      {
        model.removeParameter("Ymax");
        param = model.createParameter();
        param.initDefaults();
        param.setId("Ymax");
        param.setValue(settings.Ymax);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        symbol = pplug.createSpatialSymbolReference();
        symbol.setSpatialRef("Ymax");
        SetRequiredElements(param, false);
      }


      if (settings.UsedSymbols.Contains("Xmin"))
      {
        model.removeParameter("Xmin");
        param = model.createParameter();
        param.initDefaults();
        param.setId("Xmin");
        param.setValue(settings.Xmin);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        symbol = pplug.createSpatialSymbolReference();
        symbol.setSpatialRef("Xmin");
        SetRequiredElements(param, false);
      }

      if (settings.UsedSymbols.Contains("Ymin"))
      {
        model.removeParameter("Ymin");
        param = model.createParameter();
        param.initDefaults();
        param.setId("Ymin");
        param.setValue(settings.Ymin);
        pplug = (SpatialParameterPlugin)param.getPlugin("spatial");
        symbol = pplug.createSpatialSymbolReference();
        symbol.setSpatialRef("Ymin");
        SetRequiredElements(param, false);
      }
    }

    private List<string> OrderCompartments(libsbmlcs.Model model)
    {
      var list = new List<string>();

      var orders = new Dictionary<string, List<string>>();
      var matrix = new int[model.getNumReactions(), model.getNumCompartments()];
      var reactions = new List<string>();
      for (var i = 0; i < model.getNumReactions(); i++)
        reactions.Add(model.getReaction(i).getId());
      var compartments = new List<string>();
      for (var i = 0; i < model.getNumCompartments(); i++)
        compartments.Add(model.getCompartment(i).getId());

      for (var i = 0; i < model.getNumReactions(); i++)
      {
        var reaction = model.getReaction(i);
        var id = reaction.getId();
        for (var j = 0; j < reaction.getNumReactants(); j++)
        {
          var reference = reaction.getReactant(j);
          if (!reference.isSetSpecies()) continue;
          var species = model.getSpecies(reference.getSpecies());
          if (species == null) continue;

          if (!orders.ContainsKey(id))
            orders[id] = new List<string>();

          if (!orders[id].Contains(species.getCompartment()))
            orders[id].Add(species.getCompartment());

          matrix[reactions.IndexOf(id), compartments.IndexOf(species.getCompartment())] = 1;
        }

        for (var j = 0; j < reaction.getNumProducts(); j++)
        {
          var reference = reaction.getProduct(j);
          if (!reference.isSetSpecies()) continue;
          var species = model.getSpecies(reference.getSpecies());
          if (species == null) continue;

          if (!orders.ContainsKey(id))
            orders[id] = new List<string>();

          if (!orders[id].Contains(species.getCompartment()))
            orders[id].Add(species.getCompartment());

          matrix[reactions.IndexOf(id), compartments.IndexOf(species.getCompartment())] = 1;
        }

        for (var j = 0; j < reaction.getNumModifiers(); j++)
        {
          var reference = reaction.getModifier(j);
          if (!reference.isSetSpecies()) continue;
          var species = model.getSpecies(reference.getSpecies());
          if (species == null) continue;

          if (!orders.ContainsKey(id))
            orders[id] = new List<string>();

          if (!orders[id].Contains(species.getCompartment()))
            orders[id].Add(species.getCompartment());

          matrix[reactions.IndexOf(id), compartments.IndexOf(species.getCompartment())] = 1;
        }
      }

      foreach (var order in orders.Values)
        order.Sort();

      var uniqueOrders = new List<List<string>>();

      foreach (var order in orders.Values)
      {
        if (!Contains(uniqueOrders, order))
          uniqueOrders.Add(order);
      }

      for (var i = uniqueOrders.Count - 1; i >= 0; i--)
        if (uniqueOrders[i].Count < 2)
          uniqueOrders.RemoveAt(i);

      var counts = CountOccurances(uniqueOrders);

      var max = counts.Any() ? counts.Values.Max() : 0;

      while (max > 1)
      {
        // get first one that occurs only once
        var current = counts.FirstOrDefault(e => e.Value == 1);
        var id = current.Key;
        if (string.IsNullOrEmpty(id)) break;

        var order = RemoveIdFrom(uniqueOrders, id);
        if (list.Contains(id))
        {
          if (counts.ContainsKey(id))
            counts.Remove(id);
          continue;
        }
        list.Add(id);

        list.AddRange(order.Where(item => !item.Equals(id)));

        counts = CountOccurances(uniqueOrders);
        if (counts.ContainsKey(id))
          counts.Remove(id);
        max = counts.Any() ? counts.Values.Max() : 0;
      }

      // add remaining
      for (var i = 0; i < model.getNumCompartments(); ++i)
      {
        var current = model.getCompartment(i);
        if (!list.Contains(current.getId())) list.Add(current.getId());
      }

      return list;
    }

    private List<string> RemoveIdFrom(List<List<string>> uniqueOrders, string id)
    {
      var item = uniqueOrders.FirstOrDefault(e => e.Contains(id));
      if (item == null) return null;
      uniqueOrders.Remove(item);
      return item;
    }

    private static Dictionary<string, int> CountOccurances(List<List<string>> orders)
    {
      var counts = new Dictionary<string, int>();
      foreach (var entries in orders)
      {
        foreach (var key in entries)
        {
          if (!counts.ContainsKey(key))
            counts[key] = 0;
          counts[key]++;
        }
      }
      return counts;
    }

    private bool Contains(List<List<string>> uniqueOrders, List<string> order)
    {
      var stringOrder = Combine(order);
      foreach (var item in uniqueOrders)
      {
        if (Combine(item) == stringOrder)
          return true;
      }
      return false;
    }

    private string Combine(List<string> order)
    {
      var builder = new StringBuilder();
      for (var i = 0; i < order.Count; i++)
      {
        builder.Append(order[i]);
        if (i + 1 < order.Count)
          builder.Append(", ");
      }
      return builder.ToString();
    }

    /// <summary>
    ///   Verifies whether the reaction uses any spatial elements
    /// </summary>
    /// <param name="reaction"></param>
    /// <returns></returns>
    private List<string> GetSpeciesReferenceIdsContainedIn(Reaction reaction)
    {
      var result = new List<string>();
      if (reaction == null || reaction.getSBMLDocument() == null) return result;
      var model = reaction.getSBMLDocument().getModel();
      if (model == null) return result;
      for (var i = 0; i < reaction.getNumReactants(); ++i)
      {
        var currentRef = reaction.getReactant(i);
        var id = currentRef.getSpecies();
        var current = model.getSpecies(id);
        if (current == null) continue;
        var plug = (SpatialSpeciesPlugin)current.getPlugin("spatial");
        var isSpatial = plug != null && plug.getIsSpatial();
        if (isSpatial && !result.Contains(id))
          result.Add(id);
      }
      for (var i = 0; i < reaction.getNumProducts(); ++i)
      {
        var currentRef = reaction.getProduct(i);
        var id = currentRef.getSpecies();
        var current = model.getSpecies(id);
        if (current == null) continue;
        var plug = (SpatialSpeciesPlugin)current.getPlugin("spatial");
        var isSpatial = plug != null && plug.getIsSpatial();
        if (isSpatial && !result.Contains(id))
          result.Add(id);
      }
      for (var i = 0; i < reaction.getNumModifiers(); ++i)
      {
        var currentRef = reaction.getModifier(i);
        var id = currentRef.getSpecies();
        var current = model.getSpecies(id);
        if (current == null) continue;
        var plug = (SpatialSpeciesPlugin)current.getPlugin("spatial");
        var isSpatial = plug != null && plug.getIsSpatial();
        if (isSpatial && !result.Contains(id))
          result.Add(id);
      }
      return result;
    }

    public event EventHandler ModelChanged;

    protected virtual void OnModelChanged()
    {
      var handler = ModelChanged;
      if (handler != null) handler(this, EventArgs.Empty);
    }

    private static void SetRequiredElements(SBase sbase, bool hasAlternativeMath = true)
    {
      //var req = (RequiredElementsSBasePlugin) sbase.getPlugin("req");
      //if (req == null) return;
      //req.setCoreHasAlternateMath(hasAlternativeMath);
      //req.setMathOverridden("spatial");
    }

    public string ToMorpheus(string filename = null)
    {
      var converter = new MorpheusConverter(Document);
      return converter.ToMorpheus(filename);
    }

    public void ExportToDune(string filename)
    {
      var converter = new DuneConverter(Document);
      converter.ExportTo(filename);
    }
  }
}<file_sep>/WFDuneRunner/Program.cs
﻿using System;
using System.IO;
using System.Windows.Forms;

namespace WFDuneRunner
{
  internal static class Program
  {
    /// <summary>
    ///   The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      var form = new MainForm();

      foreach (var file in args)
      {
        if (File.Exists(file))
        {
          form.OpenFile(file);
          break;
        }
      }

      Application.Run(form);
    }
  }
}<file_sep>/LibEditSpatial/Controls/CtrlGlobal.cs
﻿using System;
using System.Windows.Forms;
using LibEditSpatial.Model;

namespace LibEditSpatial.Controls
{
  public partial class CtrlGlobal : UserControl
  {
    public CtrlGlobal()
    {
      InitializeComponent();
    }

    public GlobalConfig Model { get; set; }

    public void LoadModel(GlobalConfig model)
    {
      Model = model;

      chkWriteVTK.Checked = Model.WriteVTK;
      txtVTKname.Text = Model.VTKname;
      txtOverlap.Text = Model.Overlap.ToString();
      txtIntegrationorder.Text = Model.IntegrationOrder.ToString();
      txtSubsampling.Text = Model.SubSampling.ToString();
      if (Model.TimeStepping == "explicit")
        radExplicit.Checked = true;
      else
        radImplicit.Checked = true;
      txtExplicitsolver.Text = Model.ExplicitSolver;
      txtImplicitSovler.Text = Model.ImplicitSolver;
    }

    private void chkWriteVTK_CheckedChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.WriteVTK = chkWriteVTK.Checked;
    }

    private void txtVTKname_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.VTKname = txtVTKname.Text;
    }

    private void txtOverlap_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtOverlap.Text, out temp))
        Model.Overlap = temp;
    }

    private void txtIntegrationorder_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtIntegrationorder.Text, out temp))
        Model.IntegrationOrder = temp;
    }

    private void txtSubsampling_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      int temp;
      if (int.TryParse(txtSubsampling.Text, out temp))
        Model.SubSampling = temp;
    }

    private void radImplicit_CheckedChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      if (radImplicit.Checked)
        Model.TimeStepping = "implicit";
      else
        Model.TimeStepping = "explicit";
    }

    private void radExplicit_CheckedChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      if (radExplicit.Checked)
        Model.TimeStepping = "explicit";
      else
        Model.TimeStepping = "implicit";
    }

    private void txtExplicitsolver_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.ExplicitSolver = txtExplicitsolver.Text;
    }

    private void txtImplicitSovler_TextChanged(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.ImplicitSolver = txtImplicitSovler.Text;
    }
  }
}<file_sep>/WFEditSpatial/Controls/ControlAjacentDomains.cs
﻿using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlAdjacentDomains : BaseSpatialControl
  {
    public ControlAdjacentDomains()
    {
      InitializeComponent();
    }

    public Geometry Current { get; set; }

    public void InitializeFrom(Geometry geometry)
    {
      grid.Rows.Clear();
      Current = geometry;
      if (geometry == null) return;

      for (long i = 0; i < geometry.getNumAdjacentDomains(); ++i)
      {
        var domain = geometry.getAdjacentDomains(i);
        var spatialId = domain.getId();
        grid.Rows.Add(spatialId, domain.getDomain1(), domain.getDomain2());
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;

      for (var i = 0; i < grid.Rows.Count && i < Current.getNumAdjacentDomains(); ++i)
      {
        var domain = Current.getAdjacentDomains(i);
        var row = grid.Rows[i];
        if (domain == null) continue;

        domain.setId((string) row.Cells[0].Value);
        domain.setDomain1((string) row.Cells[1].Value);
        domain.setDomain2((string) row.Cells[2].Value);
      }

      OnModelChanged();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }
  }
}<file_sep>/WFEditSpatial/MainForm.cs
﻿using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using EditSpatial.Controls;
using EditSpatial.Forms;
using EditSpatial.Model;
using libsbmlcs;
using LibEditSpatial.Dialogs;
using LibEditSpatial.Model;
using SBW;
using SBW.Utils;

namespace EditSpatial
{
  public partial class MainForm : Form, ISBWAnalyzer
  {
    private const string NODE_COORDINATES = "nodeCoordinateComponents";
    private const string NODE_DOMAINTYPES = "nodeOfDomainTypes";
    private const string NODE_DOMAINS = "nodeOfDomains";
    private const string NODE_ADJACENTDOMAINS = "nodeOfAdjacentDomains";
    private const string NODE_GEOMS = "nodeOfGeometryDefinitions";
    private const string NODE_COMPARTMENTS = "nodeCompartments";
    private const string NODE_SPECIES = "nodeSpecies";
    private const string NODE_PARAMETERS = "nodeParameters";
    private const string NODE_REACTIONS = "nodeReactions";
    private const string NODE_RULES = "nodeRules";
    private const string NODE_INITIAL_ASSIGNMENTS = "nodeInitialAssignments";
    private const string NODE_COMPARTMENT_MAPPINGS = "nodeCompartmentMappings";
    private readonly SBWFavorites favs;
    private readonly SBWMenu menu;
    private bool haveAkira;

    public MainForm()
    {
      InitializeComponent();
      ErrorForm = new FormErrors();
      Annotation = new FormSpatialAnnotation();
      menu = new SBWMenu(mnuSBW, "Edit Spatial", () =>
      {
        if (Model == null) return "";
        if (Model.Document == null) return "";
        return libsbml.writeSBMLToString(Model.Document);
      });
      favs = new SBWFavorites(() =>
      {
        if (Model == null) return "";
        if (Model.Document == null) return "";
        return libsbml.writeSBMLToString(Model.Document);
      }, toolStrip1);

      controlInitialAssignments1.UpdateAction = () =>
      {
        if (Model.Document != null && Model.Document.getModel() != null)
          UpdateTreeWithInitialAssignments(Model.Document.getModel());
      };

      controlAdjacentDomains1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlAnalyticGeometry1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlCompartment1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlCoordinateComponents1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlDomains1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlDomainTypes1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlInitialAssignments1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlMapCompartments1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlParameters1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlRules1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlSampleFieldGeometry1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };
      controlSpecies1.ModelChanged += (e, o) =>
      { if (Model != null) Model.Dirty = true; };

      NewModel();
    }

    public static EditSpatialSettings Settings { get; set; }
    public SpatialModel Model { get; set; }
    public FormErrors ErrorForm { get; set; }
    public FormSpatialAnnotation Annotation { get; set; }

    public void doAnalysis(string model)
    {
      if (InvokeRequired)
      {
        Invoke(new doAnalysisDelegate(doAnalysis), model);
      }
      else
      {
        LoadFromString(model);
        Model.Dirty = true;
      }
    }

    public void InvalidateCoreAll()
    {
      foreach (var control in splitInitial.Panel2.Controls)
      {
        var current = control as BaseSpatialControl;
        if (current == null) continue;
        current.InvalidateSelection();
      }
    }

    public void SaveCoreAll()
    {
      foreach (var control in splitInitial.Panel2.Controls)
      {
        var current = control as BaseSpatialControl;
        if (current == null) continue;
        current.SaveChanges();
        if (current.UpdateAction != null)
          current.UpdateAction();
      }
    }

    public void InvalidateGeometryAll()
    {
      foreach (var control in splitGeometry.Panel2.Controls)
      {
        var current = control as BaseSpatialControl;
        if (current == null) continue;
        current.InvalidateSelection();
      }
    }

    public void SaveGeormetryAll()
    {
      foreach (var control in splitGeometry.Panel2.Controls)
      {
        var current = control as BaseSpatialControl;
        if (current == null) continue;
        current.SaveChanges();
        if (current.UpdateAction != null)
          current.UpdateAction();
      }
    }

    private void OnGeometrySelect(object sender, TreeViewEventArgs e)
    {
      var node = e.Node;
      SaveGeormetryAll();
      InvalidateGeometryAll();
      SelectGeometryNode(node);
    }

    private void SelectGeometryNode(TreeNode node)
    {
      if (node.Name == NODE_COORDINATES)
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = true;
        controlMapCompartments1.Visible = false;
        controlCoordinateComponents1.InitializeFrom(Model.Geometry);
      }
      else if (node.Name == NODE_DOMAINTYPES)
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = true;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = false;
        controlDomainTypes1.InitializeFrom(Model.Geometry);
      }
      else if (node.Name == NODE_DOMAINS)
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = true;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = false;
        controlDomains1.InitializeFrom(Model.Geometry);
      }
      else if (node.Name == NODE_ADJACENTDOMAINS)
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = true;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = false;
        controlAdjacentDomains1.InitializeFrom(Model.Geometry);
      }
      else if (node.Name == NODE_COMPARTMENT_MAPPINGS)
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = true;
        controlMapCompartments1.InitializeFrom(Model.Document.getModel());
      }
      else if (node.Parent != null && node.Parent.Name == NODE_GEOMS &&
               Model.Geometry != null &&
               Model.Geometry.getGeometryDefinition(node.Text) is AnalyticGeometry)
      {
        controlAnalyticGeometry1.Visible = true;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = false;
        controlAnalyticGeometry1.InitializeFrom(Model.Geometry, node.Text);
      }
      else if (node.Parent != null && node.Parent.Name == NODE_GEOMS &&
               Model.Geometry != null &&
               Model.Geometry.getGeometryDefinition(node.Text) is SampledFieldGeometry)
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = false;
        controlSampleFieldGeometry1.Visible = true;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = false;
        controlSampleFieldGeometry1.InitializeFrom(Model.Geometry, node.Text);
      }
      else
      {
        controlAnalyticGeometry1.Visible = false;
        controlAdjacentDomains1.Visible = false;
        controlDomainTypes1.Visible = false;
        controlDomains1.Visible = false;
        controlDisplayNode1.Visible = true;
        controlSampleFieldGeometry1.Visible = false;
        controlCoordinateComponents1.Visible = false;
        controlMapCompartments1.Visible = false;
        controlDisplayNode1.DisplayNode(node);
      }
    }

    private void SetFilename(string filename)
    {
      Text = string.Format("Edit Spatial: [ {0} ]", Path.GetFileName(filename));
    }

    public void UpdateUI()
    {
      if (Model == null) return;

      InvalidateGeometryAll();
      splitGeometry.Enabled = Model.IsSpatial;
      cmdAkira.Enabled = Model.IsSpatial;
      cmdPrepareDune.Enabled = Model.IsSpatial;

      FillGeometryFromModel(Model);
      FillCoreTreeFromModel(Model);

      controlDisplayNode1.ResetText();
      controlDisplayNode2.ResetText();
      txtSBML.Text = Model.ToSBML();
      txtJarnac.Text = Model.HaveModel ? Model.ToJarnac() : "";

      SetFilename(Model.FileName);

      treeCore.SelectedNode =
        treeCore.Nodes[NODE_INITIAL_ASSIGNMENTS];

      treeSpatial.SelectedNode =
        treeSpatial.Nodes[NODE_GEOMS].Nodes.Count > 0
          ? treeSpatial.Nodes[NODE_GEOMS].FirstNode
          : treeSpatial.Nodes[NODE_GEOMS];
    }

    private void ClearCoreTree()
    {
      treeCore.Nodes[NODE_COMPARTMENTS].Nodes.Clear();
      treeCore.Nodes[NODE_SPECIES].Nodes.Clear();
      treeCore.Nodes[NODE_PARAMETERS].Nodes.Clear();
      treeCore.Nodes[NODE_REACTIONS].Nodes.Clear();
      treeCore.Nodes[NODE_RULES].Nodes.Clear();
      treeCore.Nodes[NODE_INITIAL_ASSIGNMENTS].Nodes.Clear();
    }

    private void FillCoreTreeFromModel(SpatialModel spatialModel)
    {
      if (spatialModel.Document == null)
      {
        ClearCoreTree();
        return;
      }
      var model = spatialModel.Document.getModel();
      if (model == null)
      {
        ClearCoreTree();
        return;
      }


      FillTreeWithCompartments(model);
      FillTreeWithSpecies(model);
      FillTreeWithParameters(model);
      FillTreeWithReactions(model);
      FillTreeWithRules(model);
      FillTreeWithInitialAssignments(model);
    }

    private void UpdateTreeWithInitialAssignments(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_INITIAL_ASSIGNMENTS];
      foreach (TreeNode item in root.Nodes)
      {
        var current = model.getInitialAssignment(item.Text);
        if (current == null) continue;
        item.Tag = current.toSBML();
      }
    }

    private void FillTreeWithInitialAssignments(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_INITIAL_ASSIGNMENTS];
      root.Nodes.Clear();
      for (long i = 0; i < model.getNumInitialAssignments(); ++i)
      {
        var current = model.getInitialAssignment(i);
        var node = new TreeNode(current.getSymbol()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }
    }

    private void FillTreeWithRules(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_RULES];
      root.Nodes.Clear();
      for (long i = 0; i < model.getNumRules(); ++i)
      {
        var current = model.getRule(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }
    }

    private void FillTreeWithReactions(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_REACTIONS];
      root.Nodes.Clear();
      for (long i = 0; i < model.getNumReactions(); ++i)
      {
        var current = model.getReaction(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }
    }

    private void FillTreeWithParameters(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_PARAMETERS];
      root.Nodes.Clear();
      for (long i = 0; i < model.getNumParameters(); ++i)
      {
        var current = model.getParameter(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }
    }

    private void FillTreeWithSpecies(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_SPECIES];
      root.Nodes.Clear();
      for (long i = 0; i < model.getNumSpecies(); ++i)
      {
        var current = model.getSpecies(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }
    }

    private void FillTreeWithCompartments(libsbmlcs.Model model)
    {
      var root = treeCore.Nodes[NODE_COMPARTMENTS];
      root.Nodes.Clear();
      for (long i = 0; i < model.getNumCompartments(); ++i)
      {
        var current = model.getCompartment(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }
    }

    private void FillGeometryFromModel(SpatialModel model)
    {
      var geom = model.Geometry;
      if (geom == null)
      {
        treeSpatial.Nodes[NODE_COORDINATES].Nodes.Clear();
        treeSpatial.Nodes[NODE_DOMAINTYPES].Nodes.Clear();
        treeSpatial.Nodes[NODE_DOMAINS].Nodes.Clear();
        treeSpatial.Nodes[NODE_ADJACENTDOMAINS].Nodes.Clear();
        treeSpatial.Nodes[NODE_GEOMS].Nodes.Clear();
        return;
      }

      var root = treeSpatial.Nodes[NODE_COORDINATES];
      root.Nodes.Clear();
      for (long i = 0; i < geom.getNumCoordinateComponents(); ++i)
      {
        var current = geom.getCoordinateComponent(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }

      root = treeSpatial.Nodes[NODE_DOMAINTYPES];
      root.Nodes.Clear();
      for (long i = 0; i < geom.getNumDomainTypes(); ++i)
      {
        var current = geom.getDomainType(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }


      root = treeSpatial.Nodes[NODE_DOMAINS];
      root.Nodes.Clear();
      for (long i = 0; i < geom.getNumDomains(); ++i)
      {
        var current = geom.getDomain(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }

      root = treeSpatial.Nodes[NODE_ADJACENTDOMAINS];
      root.Nodes.Clear();
      for (long i = 0; i < geom.getNumAdjacentDomains(); ++i)
      {
        var current = geom.getAdjacentDomains(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};
        root.Nodes.Add(node);
      }

      root = treeSpatial.Nodes[NODE_GEOMS];
      root.Nodes.Clear();
      for (long i = 0; i < geom.getNumGeometryDefinitions(); ++i)
      {
        var current = geom.getGeometryDefinition(i);
        var node = new TreeNode(current.getId()) {Tag = current.toSBML()};

        var analytic = current as AnalyticGeometry;
        if (analytic != null)
        {
          for (long j = 0; j < analytic.getNumAnalyticVolumes(); ++j)
          {
            var vol = analytic.getAnalyticVolume(j);
            var volNode = new TreeNode(vol.getId()) {Tag = vol.toSBML()};
            node.Nodes.Add(volNode);
          }
        }

        var sample = current as SampledFieldGeometry;
        if (sample != null)
        {
          var field = geom.getSampledField(sample.getSampledField());

          if (field != null)
          {
            var fieldNode = new TreeNode(field.getId()) {Tag = field.toSBML()};
            node.Nodes.Add(fieldNode);

            for (long j = 0; j < sample.getNumSampledVolumes(); ++j)
            {
              var vol = sample.getSampledVolume(j);
              var volNode = new TreeNode(vol.getId()) {Tag = vol.toSBML()};
              node.Nodes.Add(volNode);
            }
          }
        }


        root.Nodes.Add(node);
      }
    }

    private void OnNewModel(object sender, EventArgs e)
    {
      if (SaveModelIfDirtyOrCancel()) return;
      NewModel();
    }

    private void NewModel()
    {
      Model = new SpatialModel();
      Model.ModelChanged += (o, args) => UpdateUI();
      UpdateUI();
    }

    private void OnOpenFile(object sender, EventArgs e)
    {
      if (SaveModelIfDirtyOrCancel()) return;

      var dialog = new OpenFileDialog
      {
        Title = "Open Spatial SBML",
        Filter = "SBML files|*.xml;*.sbml|All files|*.*",
        AutoUpgradeEnabled = true
      };

      if (dialog.ShowDialog() != DialogResult.OK)
        return;

      OpenFile(dialog.FileName);
    }

    public void LoadFromString(string content)
    {
      OpenModel(SpatialModel.FromString(content));
    }

    public void LoadFromJarnac(string content)
    {
      OpenModel(SpatialModel.FromJarnac(content));
    }

    public void OpenFile(string fileName)
    {
      OpenModel(SpatialModel.FromFile(fileName));
    }

    private void OpenModel(SpatialModel model)
    {
      try
      {
        Model = model;
        Model.ModelChanged += (o, args) => UpdateUI();

        UpdateUI();

        if (Model != null && Model.Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) > 0)
        {
          ShowErrors();
        }
        else
        {
          ErrorForm.Hide();
        }

        if (Model != null && !Model.IsSpatial)
        {
          Model.ConvertToL3();
          ConvertToSpatial();
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(
          string.Format("Could not load the given model.{0}{1}", Environment.NewLine, ex.Message),
          "Model could not be loaded",
          MessageBoxButtons.OK,
          MessageBoxIcon.Error
          );
      }
    }

    private void ShowErrors()
    {
      ErrorForm.InitializeFrom(Model);
      ErrorForm.Show();
    }

    private void OnSaveFile(object sender, EventArgs e)
    {
      var dialog = new SaveFileDialog
      {
        Title = "Save Spatial SBML",
        Filter = "SBML files|*.xml;*.sbml|All files|*.*",
        AutoUpgradeEnabled = true
      };

      if (dialog.ShowDialog() != DialogResult.OK)
        return;

      SaveGeormetryAll();
      Model.SaveTo(dialog.FileName);
      UpdateUI();
    }

    private void OnPrint(object sender, EventArgs e)
    {
    }

    private void OnCut(object sender, EventArgs e)
    {
    }

    private void OnCopy(object sender, EventArgs e)
    {
    }

    private void OnPaste(object sender, EventArgs e)
    {
    }

    private void OnAbout(object sender, EventArgs e)
    {
      new FormAbout().ShowDialog(this);
    }

    private void OnExit(object sender, EventArgs e)
    {
      ErrorForm.Dispose();
      ErrorForm = null;
      Application.Exit();
    }

    private void OnCoreSelect(object sender, TreeViewEventArgs e)
    {
      var node = e.Node;
      SaveCoreAll();
      InvalidateCoreAll();
      SelectCoreNode(node);
      controlDisplayNode2.DisplayNode(e.Node);
    }

    private void SelectCoreNode(TreeNode node)
    {
      if (node.Name == NODE_INITIAL_ASSIGNMENTS)
      {
        controlDisplayNode2.Visible = false;
        controlInitialAssignments1.Visible = true;
        controlInitialAssignments1.InitializeFrom(Model.Document);
        controlParameters1.Visible = false;
        controlSpecies1.Visible = false;
        controlCompartment1.Visible = false;
        controlRules1.Visible = false;
      }
      else if (node.Name == NODE_PARAMETERS)
      {
        controlDisplayNode2.Visible = false;
        controlInitialAssignments1.Visible = false;
        controlParameters1.Visible = true;
        controlSpecies1.Visible = false;
        controlCompartment1.Visible = false;
        controlRules1.Visible = false;
        controlParameters1.InitializeFrom(Model.Document);
      }
      else if (node.Name == NODE_SPECIES)
      {
        controlDisplayNode2.Visible = false;
        controlInitialAssignments1.Visible = false;
        controlParameters1.Visible = false;
        controlSpecies1.Visible = true;
        controlCompartment1.Visible = false;
        controlSpecies1.InitializeFrom(Model.Document);
        controlRules1.Visible = false;
      }
      else if (node.Name == NODE_COMPARTMENTS)
      {
        controlDisplayNode2.Visible = false;
        controlInitialAssignments1.Visible = false;
        controlParameters1.Visible = false;
        controlSpecies1.Visible = false;
        controlCompartment1.Visible = true;
        controlCompartment1.InitializeFrom(Model.Document);
        controlRules1.Visible = false;
      }
      else if (node.Name == NODE_RULES)
      {
        controlDisplayNode2.Visible = false;
        controlInitialAssignments1.Visible = false;
        controlParameters1.Visible = false;
        controlSpecies1.Visible = false;
        controlCompartment1.Visible = false;
        controlRules1.Visible = true;
        controlRules1.InitializeFrom(Model.Document);
      }
      else
      {
        controlDisplayNode2.Visible = true;
        controlInitialAssignments1.Visible = false;
        controlParameters1.Visible = false;
        controlSpecies1.Visible = false;
        controlCompartment1.Visible = false;
        controlRules1.Visible = false;
        controlDisplayNode2.DisplayNode(node);
      }
    }

    private void OnLoad(object sender, EventArgs e)
    {
      ReadSettings();

      var remove = false;
      haveAkira = false;
      try
      {
        favs.Update();
        menu.UpdateSBWMenu();

        SBWExporter.SetupImport(
          mnuImport, doAnalysis, s => SetFilename(s));

        foreach (ToolStripItem item in mnuSBW.DropDownItems)
        {
          haveAkira |= haveAkira || item.Text == "Spatial SBML";
        }
      }
      catch
      {
        remove = true;
      }

      if (remove || mnuSBW.DropDownItems.Count == 0)
      {
        favs.RemoveFromToolStrip();
        mnuSBW.Visible = false;
        mnuExportDune.Visible = false;
      }

      cmdAkira.Visible = haveAkira;
    }

    public void ReadSettings()
    {
      Settings = EditSpatialSettings.GetDefault(Size);

      if (Settings.Width < Screen.FromControl(this).WorkingArea.Width &&
          Settings.Height < Screen.FromControl(this).WorkingArea.Height)
        Size = new Size(Settings.Width, Settings.Height);

      chkMultipleCompartments.Checked = Settings.IgnoreMultiCompartments;
    }

    private void OnFormClosed(object sender, FormClosedEventArgs e)
    {
      WriteSettings();
    }

    private void WriteSettings()
    {
      Settings.Save();
    }

    private void OnShowWarnings(object sender, EventArgs e)
    {
      ShowErrors();
    }

    private void OnApplyJarnacClick(object sender, EventArgs e)
    {
      LoadFromJarnac(txtJarnac.Text);
    }

    private void OnExportMorpheusClick(object sender, EventArgs e)
    {
      if (Model == null || Model.Document == null)
        return;
      Model.Document.clearValidators();
      Model.Document.addValidator(SpatialModel.CustomSpatialValidator);
      Model.Document.checkConsistency();
      if (Model.Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) > 0)
      {
        MessageBox.Show(
          "Unfortunately, the SBML model contains a number of errors. These need to be corrected, before the model can be exported to Morpheus.",
          "Invalid Model", MessageBoxButtons.OK, MessageBoxIcon.Error);
        ShowErrors();
        return;
      }

      var dialog = new SaveFileDialog
      {
        Title = "Save Morpheus Configuration File",
        Filter = "Configuration file|*.xml|All files|*.*",
        AutoUpgradeEnabled = true
      };

      if (dialog.ShowDialog() != DialogResult.OK)
        return;

      ExportMorpheusFile(dialog.FileName);
    }

    public void ExportMorpheusFile(string fileName)
    {
      File.WriteAllText(fileName, Model.ToMorpheus(fileName));
    }

    private void ConvertToSpatial()
    {
      var dialog = new FormInitSpatial { SpatialModel = Model };
      if (dialog.ShowDialog(this) == DialogResult.OK)
      {
        var selection = dialog.CreateModel;

        if (!Model.ConvertToSpatial(selection))
        {
          ShowErrors();
        }
        else
        {
          UpdateUI();
        }
      }
    }

    private void OnShowSpatialWizard(object sender, EventArgs e)
    {
      ConvertToSpatial();
    }

    private void OnExportDuneSBMLClick(object sender, EventArgs e)
    {
      if (Model == null || Model.Document == null)
        return;
      Model.Document.clearValidators();
      Model.Document.addValidator(SpatialModel.CustomSpatialValidator);
      Model.Document.checkConsistency();
      if (Model.Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) > 0)
      {
        MessageBox.Show(
          "Unfortunately, the SBML model contains a number of errors. These need to be corrected, before the model can be exported to Dune.",
          "Invalid Model", MessageBoxButtons.OK, MessageBoxIcon.Error);
        ShowErrors();
        return;
      }

      var dialog = new SaveFileDialog
      {
        Title = "Export DUNE SBML Model",
        Filter = "SBML files|*.xml;*.sbml|All files|*.*",
        AutoUpgradeEnabled = true
      };

      if (dialog.ShowDialog() != DialogResult.OK)
        return;

      Model.ExportDuneSBML(dialog.FileName);
    }

    private void OnExportDuneClick(object sender, EventArgs e)
    {
      if (Model == null || Model.Document == null)
        return;
      Model.Document.clearValidators();
      Model.Document.addValidator(SpatialModel.CustomSpatialValidator);
      Model.Document.checkConsistency();
      if (Model.Document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) > 0)
      {
        MessageBox.Show(
          "Unfortunately, the SBML model contains a number of errors. These need to be corrected, before the model can be exported to Dune.",
          "Invalid Model", MessageBoxButtons.OK, MessageBoxIcon.Error);
        ShowErrors();
        return;
      }

      var dialog = new SaveFileDialog
      {
        Title = "Export Model to DUNE",
        Filter = "Main Implementation file|*.cc|All files|*.*",
        AutoUpgradeEnabled = true
      };

      if (dialog.ShowDialog() != DialogResult.OK)
        return;

      Model.ExportToDune(dialog.FileName);
    }

    private void OnAddSampledFieldGeometry(object sender, EventArgs e)
    {
      if (Model.Geometry == null) return;

      var geom = Model.Geometry.createSampledFieldGeometry();
      geom.setId(String.Format("sampledFieldGeometry{0}", Model.Geometry.GetNumSampledFieldGeometries()));

      Model.Dirty = true;
      UpdateUI();
    }

    private void OnAddAnalyticGeometry(object sender, EventArgs e)
    {
      if (Model.Geometry == null) return;

      var geom = Model.Geometry.createAnalyticGeometry();
      geom.setId(String.Format("analyticGeometry{0}", Model.Geometry.GetNumAnalyticGeometries()));
      
      Model.Dirty = true;
      UpdateUI();
    }

    private void OnSpatialItemDeleteClick(object sender, EventArgs e)
    {
      var selected = treeSpatial.SelectedNode;
      if (selected == null || selected.Level == 0) return;

      var selectedId = selected.Text;
      var parent = selected.Parent;
      if (Model.Geometry == null) return;

      Model.Dirty = true;

      if (selected.Level == 1)
        switch (parent.Name)
        {
          case NODE_DOMAINS:
          {
            Model.Geometry.removeDomain(selectedId);
            UpdateUI();
            break;
          }
          case NODE_DOMAINTYPES:
          {
            Model.Geometry.removeDomainType(selectedId);
            UpdateUI();
            break;
          }
          case NODE_GEOMS:
          {
            Model.Geometry.removeGeometryDefinition(selectedId);
            UpdateUI();
            break;
          }
          case NODE_ADJACENTDOMAINS:
          {
            Model.Geometry.removeAdjacentDomains(selectedId);
            UpdateUI();
            break;
          }
          case NODE_COORDINATES:
          {
            Model.Geometry.removeCoordinateComponent(selectedId);
            UpdateUI();
            break;
          }
        }

      if (selected.Level == 2)
      {
        switch (parent.Parent.Name)
        {
          case NODE_GEOMS:
          {
            var geom = Model.Geometry.getGeometryDefinition(parent.Text);
            if (geom is AnalyticGeometry)
            {
              var ageom = geom as AnalyticGeometry;
              ageom.removeAnalyticVolume(selectedId);
              UpdateUI();
            }
            else if (geom is SampledFieldGeometry)
            {
              var sgeom = geom as SampledFieldGeometry;
              sgeom.removeSampledVolume(selectedId);
              UpdateUI();
            }
            break;
          }
        }
      }

      treeSpatial.SelectedNode = parent;
    }

    private void OnCoreItemDeleteClick(object sender, EventArgs e)
    {
      var selected = treeCore.SelectedNode;
      if (selected == null || selected.Level == 0) return;

      var selectedId = selected.Text;
      var parent = selected.Parent;
      switch (parent.Name)
      {
        case NODE_PARAMETERS:
        {
          Model.Document.getModel().removeParameter(selectedId);
          UpdateUI();
          break;
        }
        case NODE_COMPARTMENTS:
        {
          Model.Document.getModel().removeCompartment(selectedId);
          UpdateUI();
          break;
        }
        case NODE_SPECIES:
        {
          Model.Document.getModel().removeSpecies(selectedId);
          UpdateUI();
          break;
        }
        case NODE_REACTIONS:
        {
          Model.Document.getModel().removeSpecies(selectedId);
          UpdateUI();
          break;
        }
        case NODE_RULES:
        {
          Model.Document.getModel().removeRule(selectedId);
          UpdateUI();
          break;
        }
        case NODE_INITIAL_ASSIGNMENTS:
        {
          Model.Document.getModel().removeInitialAssignment(selectedId);
          UpdateUI();
          break;
        }
      }
      treeCore.SelectedNode = parent;
    }

    private void OnMoveARtoIAClick(object sender, EventArgs e)
    {
      if (Model == null) return;
      Model.MoveAllRulesToAssignments();

      Model.Dirty = true;
      UpdateUI();
    }

    private void OnEditSpatialAnnotationClick(object sender, EventArgs e)
    {
      if (Model == null || Model.Document == null) return;

      Annotation.InitFrom(Model);

      Annotation.ShowDialog(this);
      if (Annotation.Status == DialogResult.OK)
      {
        Annotation.SaveToModel(Model.Document.getModel());
      }
    }

    private void OnSimulateWithAkiraClick(object sender, EventArgs e)
    {
      if (!haveAkira) return;
      if (Model == null || Model.Document == null) return;
      if (!Model.IsSpatial) return;
      try
      {
        HighLevel.Send("Spatial SBML", "Spatial SBML", "void doAnalysis(string)", Model.ToSBML());
      }
      catch
      {
      }
    }

    private void OnCheckMultipleCompartmentsClick(object sender, EventArgs e)
    {
      Settings.IgnoreMultiCompartments = chkMultipleCompartments.Checked;
      Settings.Save();
    }

    private void OnEditPreferencesClick(object sender, EventArgs e)
    {
      using (var dlg = new FormSettings {Settings = Settings})
      {
        if (dlg.ShowDialog(this) == DialogResult.OK)
        {
          dlg.Settings.Save();
          ReadSettings();
        }
      }
    }

    private void OnPrepareDuneClick(object sender, EventArgs e)
    {
      if (Model == null || Model.Document == null) return;
      if (!Model.IsSpatial) return;

      using (var dlg = new FormPrepareDune
      {
        Settings = Settings,
        Model = Model,
        TargetDir = Settings.DefaultDir,
        ModuleName = Path.GetFileNameWithoutExtension(Model.FileName)
      })
      {
        dlg.ShowDialog(this);
      }
    }

    #region Drag / Drop

    private void MainForm_DragDrop(object sender, DragEventArgs e)
    {
      try
      {
        var sFilenames = (string[]) e.Data.GetData(DataFormats.FileDrop);
        var oInfo = new FileInfo(sFilenames[0]);
        if (oInfo.Extension.ToLower() == ".xml" || oInfo.Extension.ToLower() == ".sbml")
        {
          OpenFile(sFilenames[0]);
        }
      }
      catch (Exception)
      {
      }
    }

    private void MainForm_DragEnter(object sender, DragEventArgs e)
    {
      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
        var sFilenames = (string[]) e.Data.GetData(DataFormats.FileDrop);
        var oInfo = new FileInfo(sFilenames[0]);
        if (oInfo.Extension.ToLower() == ".xml" || oInfo.Extension.ToLower() == ".sbml")
        {
          e.Effect = DragDropEffects.Copy;
          return;
        }
      }
      e.Effect = DragDropEffects.None;
    }

    #endregion


    /// <summary>
    /// This function asks a user whether the model should be saved
    /// </summary>
    /// <returns>true, if model is dirty and the user pressed cancel, false otherwise</returns>
    private bool SaveModelIfDirtyOrCancel()
    {
      if (Model == null || !Model.Dirty) return false;

      DialogResult result =
        MessageBox.Show(this,
          "There are unsaved changes in the model. Would you like to save them?",
          "Save changes?",
          MessageBoxButtons.YesNoCancel,
          MessageBoxIcon.Question,
          MessageBoxDefaultButton.Button3);

      if (result == DialogResult.Cancel)
      {
        return true;
      }

      if (result == DialogResult.Yes)
      {
        OnSaveFile(this, EventArgs.Empty);
      }

      return false;
    }

    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
      e.Cancel = SaveModelIfDirtyOrCancel();
    }
  }
}
<file_sep>/WFEditSpatial/Forms/FormSpatialAnnotation.cs
﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Forms;
using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Forms
{
  public partial class FormSpatialAnnotation : Form
  {
    public const string SPATIAL_ANNOTATION_URL = "http://fbergmann.github.io/spatial-sbml/settings";
    private readonly Random rand;

    public FormSpatialAnnotation()
    {
      InitializeComponent();
      rand = new Random();
    }

    public libsbmlcs.Model Model { get; set; }
    public DialogResult Status { get; set; }

    private void InitializeIdCombo(libsbmlcs.Model model)
    {
      colId.Items.Clear();
      for (var i = 0; i < model.getNumSpecies(); i++)
      {
        colId.Items.Add(model.getSpecies(i).getId());
      }
    }

    private XMLNode getAnnotationNode(libsbmlcs.Model model, string ns)
    {
      if (model == null || !model.isSetAnnotation()) return null;

      var parent = model.getAnnotation();
      var again = true;
      while (again)
      {
        again = false;
        for (var i = 0; i < parent.getNumChildren(); ++i)
        {
          var current = parent.getChild(i);
          if (current.getName() == "annotation")
          {
            again = true;
            parent = current;
            break;
          }

          if (current.hasNamespaceURI(ns))
            return current;
        }
      }

      return null;
    }

    private string Clean(string original)
    {
      var result = original.Replace("<notes>", "");
      result = result.Replace("</notes>", "");
      result = result.Replace("</body>", "");
      result = result.Replace("<pre>", "");
      result = result.Replace("</pre>", "");
      result = result.Replace("<body xmlns=\"http://www.w3.org/1999/xhtml\">", "");
      result = result.Replace("\n", "");
      result = result.Trim();
      return result;
    }

    private void AddRowForNote(string text, Species species)
    {
      text = Clean(text);

      if (string.IsNullOrWhiteSpace(text)) return;
      if (!text.Contains("spatial")) return;


      var palette = "black-blue";
      if (text.Contains("GFP"))
        palette = "black-green";
      if (text.Contains("RFP"))
        palette = "black-red";

      double max = 6;
      double scale = 10;
      if (species.isSetInitialConcentration() && species.getInitialConcentration() > 0)
        max = scale*species.getInitialConcentration();
      else if (species.isSetInitialAmount() && species.getInitialAmount() > 0)
        max = scale*species.getInitialAmount();


      grid.Rows.Add(species.getId(), palette, max.ToString(CultureInfo.InvariantCulture));
    }

    private void InitializeFromNotes(libsbmlcs.Model model)
    {
      grid.Rows.Clear();

      if (model == null) return;
      Model = model;
      for (var i = 0; i < model.getNumSpecies(); ++i)
      {
        var current = model.getSpecies(i);
        if (current == null || !current.isSetNotes()) continue;


        AddRowForNote(current.getNotesString(), current);
      }
    }

    private void InitializeFromAnnotation(libsbmlcs.Model model)
    {
      var node = getAnnotationNode(model, SPATIAL_ANNOTATION_URL);
      if (node == null) return;

      var update = node.getChild("update");
      if (update.getName() == "update")
      {
        txtStep.Text = update.getAttrValue("step");
        txtUpdate.Text = update.getAttrValue("freq");
      }

      grid.Rows.Clear();

      var items = node.getChild("items");
      if (items.getName() == "items")
      {
        for (var i = 0; i < items.getNumChildren(); ++i)
        {
          var item = items.getChild(i);

          var id = item.getAttrValue("sbmlId");
          var palette = item.getAttrValue("palette");
          var max = item.getAttrValue("max");

          grid.Rows.Add(id, palette, max);
        }
      }
    }

    public void SaveToModel(libsbmlcs.Model model)
    {
      if (model == null) return;

      var node = new XMLNode(new XMLTriple("spatialInfo", SPATIAL_ANNOTATION_URL, ""), new XMLAttributes());
      node.addAttr("xmlns", SPATIAL_ANNOTATION_URL);

      // save stepsize
      var update = new XMLNode(new XMLTriple("update", SPATIAL_ANNOTATION_URL, ""), new XMLAttributes());
      update.addAttr("step", txtStep.Text);
      update.addAttr("freq", txtUpdate.Text);

      node.addChild(update);

      // save assignments
      if (grid.Rows.Count > 0)
      {
        var items = new XMLNode(new XMLTriple("items", SPATIAL_ANNOTATION_URL, ""), new XMLAttributes());

        for (var i = 0; i < grid.Rows.Count; ++i)
        {
          var current = grid.Rows[i];
          var item = new XMLNode(new XMLTriple("item", SPATIAL_ANNOTATION_URL, ""), new XMLAttributes());
          var id = current.Cells[0].Value as string;
          if (string.IsNullOrWhiteSpace(id)) continue;
          item.addAttr("sbmlId", id);
          item.addAttr("palette", current.Cells[1].Value as string);
          item.addAttr("max", current.Cells[2].Value as string);
          items.addChild(item);
        }
        node.addChild(items);
      }

      if (model.isSetAnnotation())
      {
        var annot = model.getAnnotation();
        var num = (int) annot.getNumChildren();
        for (var i = num - 1; i >= 0; i--)
        {
          var child = annot.getChild(i);
          if (child.getName() == "spatialInfo" &&
              (child.getNamespaceURI() == SPATIAL_ANNOTATION_URL || child.getNamespaceURI() == ""))
            annot.removeChild(i);
        }
      }
      model.removeTopLevelAnnotationElement("spatialInfo", SPATIAL_ANNOTATION_URL, false);
      model.appendAnnotation(node);
    }

    public void InitFrom(SpatialModel spatialModel)
    {
      Status = DialogResult.Cancel;
      colId.Items.Clear();
      grid.Rows.Clear();

      if (spatialModel == null || spatialModel.Document == null || spatialModel.Document.getModel() == null)
        return;

      Model = spatialModel.Document.getModel();
      if (Model == null)
        return;

      InitializeIdCombo(Model);

      InitializeFromAnnotation(Model);

      if (grid.Rows.Count < 2)
        InitializeFromNotes(Model);
    }

    private void FormSpatialAnnotation_FormClosing(object sender, FormClosingEventArgs e)
    {
      e.Cancel = true;
      Hide();
    }

    private void OnInitFromNotesClick(object sender, EventArgs e)
    {
      InitializeFromNotes(Model);
    }

    private void OnClearClick(object sender, EventArgs e)
    {
      grid.Rows.Clear();
    }

    private void cmdOK_Click(object sender, EventArgs e)
    {
      Status = DialogResult.OK;
    }

    private void cmdCombine_Click(object sender, EventArgs e)
    {
      var selected = grid.SelectedRows;
      if (selected == null || selected.Count < 2) return;

      // get common properties
      var palette = selected[0].Cells[1].Value as string;
      var max = selected[0].Cells[2].Value as string;

      // remove rows 
      var ids = new List<string>();
      for (var i = selected.Count - 1; i >= 0; i--)
      {
        ids.Insert(0, selected[i].Cells[0].Value as string);
        grid.Rows.Remove(selected[i]);
      }

      // add species with assignment rule 
      var species = Model.createSpecies();
      species.initDefaults();

      var name = "combined_" + ids[0];
      var formula = ids[0];
      colId.Items.Remove(ids[0]);
      for (var i = 1; i < ids.Count; i++)
      {
        name = name + "_" + ids[i];
        formula = formula + " + " + ids[i];
        colId.Items.Remove(ids[i]);
      }

      species.setId(name);
      species.setCompartment(Model.getSpecies(ids[0]).getCompartment());
      var plug = species.getPlugin("spatial") as SpatialSpeciesPlugin;
      if (plug != null)
        plug.setIsSpatial(true);

      var assignment = Model.createAssignmentRule();
      assignment.setVariable(species.getId());
      assignment.setFormula(formula);

      colId.Items.Add(species.getId());

      // add new row
      var index = grid.Rows.Add(species.getId(), palette, max);

      // select row
      grid.Rows[index].Selected = true;
    }

    private string getRandomPalette()
    {
      switch (rand.Next(3))
      {
        default:
          return "black-green";
        case 2:
          return "black-red";
        case 1:
          return "black-blue";
      }
    }

    private void OnAddAllClick(object sender, EventArgs e)
    {
      OnClearClick(sender, e);

      for (var i = 0; i < Model.getNumSpecies(); i++)
      {
        var species = Model.getSpecies(i);
        double max = 6;
        double scale = 10;
        if (species.isSetInitialConcentration() && species.getInitialConcentration() > 0)
          max = scale*species.getInitialConcentration();
        else if (species.isSetInitialAmount() && species.getInitialAmount() > 0)
          max = scale*species.getInitialAmount();

        grid.Rows.Add(species.getId(), getRandomPalette(), max);
      }
    }
  }
}<file_sep>/WFEditDMP/MainForm.cs
﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using LibEditSpatial.Dialogs;
using LibEditSpatial.Model;
using WFEditDMP.Forms;

namespace WFEditDMP
{
  public partial class MainForm : Form
  {
    public MainForm()
    {
      InitializeComponent();
      NewModel();

      ctrlPalette1.PalleteValueChanged += (o, args) => { dmpRenderControl1.CurrentValue = args.Value; };
      ctrlPalette1.PalleteChanged += (o, args) =>
      {
        if (Model != null)
        {
          Model.Palette = args;
          UpdateUI();
        }
        DmpPalette.Default = args;
      };

      dmpRenderControl1.IndexLocationChanged +=
        (o, args) => { lblPosition.Text = string.Format("nx={0}, ny={1}", args.X, args.Y); };
      dmpRenderControl1.DataLocationChanged +=
        (o, args) => { lblData.Text = string.Format("x={0}, y={1}", args.X, args.Y); };
    }

    public DmpModel Model { get; set; }
    public string LastOpenDir { get; set; }
    public String[] PaletteFiles { get; set; }
    public string CurrentPalette { get; set; }

    private void SetTitle(string fileName)
    {
      if (!string.IsNullOrEmpty(fileName))
        Text = String.Format("Edit DMP - [{0}]", Path.GetFileName(fileName));
      else
        Text = "Edit DMP";
    }

    private void UpdateUI()
    {
      if (Model == null)
      {
        SetTitle(null);
        lblMessage.Text = "no model";
        return;
      }

      lblSize.Text = string.Format("{0} x {1}", Model.Columns, Model.Rows);
      txtCols.Text = Model.Columns.ToString();
      txtRows.Text = Model.Rows.ToString();
      txtMinX.Text = Model.MinX.ToString(CultureInfo.InvariantCulture);
      txtMaxX.Text = Model.MaxX.ToString(CultureInfo.InvariantCulture);
      txtMinY.Text = Model.MinY.ToString(CultureInfo.InvariantCulture);
      txtMaxY.Text = Model.MaxY.ToString(CultureInfo.InvariantCulture);
      ctrlPalette1.UpdateValues(Model.Min, ctrlPalette1.Current.Value, Model.Max);
      dmpRenderControl1.LoadModel(Model);
      SetTitle(Model.FileName);
    }

    public void OpenFile(string filename)
    {
      LastOpenDir = Path.GetDirectoryName(filename);           
      Model = DmpModel.FromFile(filename);
      ctrlPalette1.UpdateValues(Model.Min, ctrlPalette1.Current.Value, Model.Max);
      dmpRenderControl1.CurrentValue = Model.Max;
      Model.Palette = ctrlPalette1.Palette;
      UpdateUI();
    }

    private void NewModel()
    {
      NewModel(new Size(50, 50), new RectangleF(0, 50, 0, 50));
    }

    private void NewModel(Size size, RectangleF rect)
    {
      Model = new DmpModel(50, 50);
      ctrlPalette1.UpdateValues(Model.Min, ctrlPalette1.Current.Value, Model.Max);
      dmpRenderControl1.CurrentValue = Model.Max;
      Model.Palette = ctrlPalette1.Palette;
      UpdateUI();
    }

    private void OnNewClick(object sender, EventArgs e)
    {
      if (SaveModelIfDirtyOrCancel())
        return;

      using (var dlg = new FormResize { 
        Dimensions = new Size(Model.Columns, Model.Rows),
        CanvasBounds = new RectangleF((float)Model.MinX, (float)Model.MinY, (float)Model.MaxX, (float)Model.MaxY)
      } )
      { 
        if (dlg.ShowDialog(this) == DialogResult.OK)
        { 
          NewModel(dlg.Dimensions, dlg.CanvasBounds);
        }
      }
    }

    private void OnOpenClick(object sender, EventArgs e)
    {
      if (SaveModelIfDirtyOrCancel())
        return;

      using (var dialog = new OpenFileDialog
      {
        Title = "Open file",
        Filter = "DMP files|*.dmp|Image files|*.tif;*.tiff;*.png;*.jpg;*.jpeg;*.bmp|All files|*.*",
        AutoUpgradeEnabled = true,
        InitialDirectory = LastOpenDir
      })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
        {          
          OpenFile(dialog.FileName);
        }
      }
    }

    

    private void OnSaveClick(object sender, EventArgs e)
    {
      if (string.IsNullOrWhiteSpace(Model.FileName))
      {
        OnSaveAsClick(sender, e);
        return;
      }
      SaveAs(Model.FileName);
    }

    private void OnExitClick(object sender, EventArgs e)
    {
      Close();
    }

    private void OnAboutClick(object sender, EventArgs e)
    {
      using (var dlg = new AboutBox())
      {
        dlg.ShowDialog();
      }
    }

    private void SaveAs(string fileName)
    {
      if (Model == null) return;

      string lower = fileName.ToLowerInvariant();
      if (lower.EndsWith(".tif") || lower.EndsWith(".tiff"))
        Model.ToImage().Save(fileName);
      else 
        Model.SaveAs(fileName);
      UpdateUI();
    }

    private void OnSaveAsClick(object sender, EventArgs e)
    {
      if (Model == null) return;
      using (var dialog = new SaveFileDialog
      {
        Title = "Save file",
        Filter = "DMP files|*.dmp|TIFF files|*.tif;*.tiff|All files|*.*",
        AutoUpgradeEnabled = true,
        InitialDirectory = LastOpenDir
      })
      {
        if (dialog.ShowDialog() == DialogResult.OK)
          SaveAs(dialog.FileName);
      }
    }

    private void LoadPalettes(string baseDirectory)
    {
      PaletteFiles = Directory.GetFiles(baseDirectory, "*.txt", SearchOption.TopDirectoryOnly);
      cmbPalettes.Items.Clear();
      cmbPalettes.Items.Add("Default");
      foreach (var file in PaletteFiles)
      {
        cmbPalettes.Items.Add(Path.GetFileNameWithoutExtension(file));
      }
    }

    private void OnLoad(object sender, EventArgs e)
    {
      ctrlPalette1.Palette = DmpPalette.Default;

      LoadPalettes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Palettes"));
    }

    private void OnPaletteChanged(object sender, EventArgs e)
    {
      var index = cmbPalettes.SelectedIndex;

      if (index < 0) return;

      if ((string) cmbPalettes.Items[index] == CurrentPalette)
        return;

      if (index == 0)
      {
        ctrlPalette1.ChangePalette(DmpPalette.Default);
      }
      else
        try
        {
          ctrlPalette1.ChangePalette(PaletteFiles[index - 1]);
        }
        catch
        {
        }
      CurrentPalette = (string) cmbPalettes.Items[index];
    }

    private void OnSizeChanged(object sender, EventArgs e)
    {
      int val;

      if (int.TryParse(txtSize.Text, out val))
      {
        dmpRenderControl1.PencilSize = val;
      }
    }

    private void OnResizeClicked(object sender, EventArgs e)
    {
      int rows, cols;
      if (int.TryParse(txtCols.Text, out cols) && int.TryParse(txtRows.Text, out rows))
      {
        Model.Resize(cols, rows);
        UpdateUI();
      }
    }

    private void OnRotateLeft(object sender, EventArgs e)
    {
      Model.RotateRight();
      Model.RotateRight();
      Model.RotateRight();
      UpdateUI();
    }

    private void OnRotateRight(object sender, EventArgs e)
    {
      Model.RotateRight();
      UpdateUI();
    }

    private void OnCenterClick(object sender, EventArgs e)
    {
      Model.Center();
      UpdateUI();
    }

    private void OnMinXChanged(object sender, EventArgs e)
    {
      double temp;
      if (Model != null && double.TryParse(txtMinX.Text, out temp))
        Model.MinX = temp;
    }

    private void OnMaxXChanged(object sender, EventArgs e)
    {
      double temp;
      if (Model != null && double.TryParse(txtMaxX.Text, out temp))
        Model.MaxX = temp;
    }

    private void OnMinYChanged(object sender, EventArgs e)
    {
      double temp;
      if (Model != null && double.TryParse(txtMinY.Text, out temp))
        Model.MinY = temp;
    }

    private void OnMaxYChanged(object sender, EventArgs e)
    {
      double temp;
      if (Model != null && double.TryParse(txtMaxY.Text, out temp))
        Model.MaxY = temp;
    }

    private void OnAdjustClick(object sender, EventArgs e)
    {
      if (Model == null) return;
      var range = Model.Range;

      using (var dlg = new FormAdjustDmp())
      {
        dlg.InitializeFrom(range, (DmpModel) Model.Clone());
        if (dlg.ShowDialog(this) == DialogResult.OK)
        {
          Model = dlg.Model;
          UpdateUI();
        }
      }
    }

    private int FindHCF(int m, int n)
    {
      int temp, reminder;
      if (m < n)
      {
        temp = m;
        m = n;
        n = temp;
      }
      while (true)
      {
        reminder = m % n;
        if (reminder == 0)
          return n;
        else
          m = n;
        n = reminder;
      }
    }

    private void OnAspectClick(object sender, EventArgs e)
    {
      // resizes the window to match the aspect ratio
      var screen = Screen.FromControl(this);
      StartPosition = FormStartPosition.Manual;
      var bounds = screen.Bounds;
      Location = bounds.Location;

      int hcf = FindHCF((int)Math.Round(Model.MaxX), (int)Math.Round(Model.MaxY));

      double width = Math.Round(Model.MaxX);
      double height = Math.Round(Model.MaxY);

      double factorX = width / (double)hcf;
      double factorY = height / (double)hcf;

      double maxWidth = bounds.Width - 50;
      double maxHeight = bounds.Height - 50;
      double min = Math.Min(maxWidth, maxHeight);

      if (factorX > factorY)
      {
        double newHeight = height / width * min;
        Size = new System.Drawing.Size((int)min, (int)newHeight);
      }
      else
      {
        double newWidth = width / height * min;
        Size = new System.Drawing.Size((int)newWidth, (int)min);
      }
    }

    /// <summary>
    /// This function asks a user whether the model should be saved
    /// </summary>
    /// <returns>true, if model is dirty and the user pressed cancel, false otherwise</returns>
    private bool SaveModelIfDirtyOrCancel()
    {
      if (Model == null || !Model.Dirty) return false;

      DialogResult result =
        MessageBox.Show(this,
          "There are unsaved changes in the model. Would you like to save them?",
          "Save changes?",
          MessageBoxButtons.YesNoCancel,
          MessageBoxIcon.Question,
          MessageBoxDefaultButton.Button3);

      if (result == DialogResult.Cancel)
      {
        return true;
      }

      if (result == DialogResult.Yes)
      {
        OnSaveClick(this, EventArgs.Empty);
      }

      return false;
    }
    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
      e.Cancel = SaveModelIfDirtyOrCancel();
    }

    private void OnResizeClick(object sender, EventArgs e)
    {
      using (var dlg = new FormResize
      {
        Dimensions = new Size(Model.Columns, Model.Rows),
        CanvasBounds = new RectangleF((float) Model.MinX, (float) Model.MinY, (float) Model.MaxX, (float) Model.MaxY)
      })
      {
        if (dlg.ShowDialog(this) == DialogResult.OK)
        {
          Model.Resize(dlg.Dimensions.Width, dlg.Dimensions.Height, dlg.ScaleContents);
          Model.MinX = dlg.CanvasBounds.X;
          Model.MinY = dlg.CanvasBounds.Y;
          Model.MaxX = dlg.CanvasBounds.Width;
          Model.MaxY = dlg.CanvasBounds.Height;
          UpdateUI();
        }
      }
    }

    private void OnMaskWithCompartmentClick(object sender, EventArgs e)
    {
      using (var dlg = new OpenFileDialog { 
        Title = "Choose DMP file to mask with. ",
        Filter = "All supported|*.dmp;*.tif;*.tiff;*.png;*.jpg;*.jpeg;*.bmp|DMP files|*.dmp|Image files|*.tif;*.tiff;*.png;*.jpg;*.jpeg;*.bmp|All files|*.*",
        AutoUpgradeEnabled = true, 
        InitialDirectory = this.LastOpenDir})
      {
        if (dlg.ShowDialog() == DialogResult.OK)
        {
          try
          {
            var file = DmpModel.FromFile(dlg.FileName);
            Model.MaskWith(file);
            UpdateUI();
          }
          catch
          {
            MessageBox.Show("Masking failed, ensure that the files have precisely the same dimensions", "Masking failed",
              MessageBoxButtons.OK, MessageBoxIcon.Error);
          }
        }
      }
    }

    #region Drag / Drop

    private void MainForm_DragDrop(object sender, DragEventArgs e)
    {
      try
      {
        var sFilenames = (string[])e.Data.GetData(DataFormats.FileDrop);
        var oInfo = new FileInfo(sFilenames[0]);
        if (oInfo.Extension.ToLower() == ".dmp" || DmpModel.IsImageFile(sFilenames[0]))
        {
          OpenFile(sFilenames[0]);
        }
      }
      catch (Exception)
      {
      }
    }

    private void MainForm_DragEnter(object sender, DragEventArgs e)
    {
      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
        var sFilenames = (string[])e.Data.GetData(DataFormats.FileDrop);
        var oInfo = new FileInfo(sFilenames[0]);
        if (oInfo.Extension.ToLower() == ".dmp" || DmpModel.IsImageFile(sFilenames[0]))
        {
          e.Effect = DragDropEffects.Copy;
          return;
        }
      }
      e.Effect = DragDropEffects.None;
    }

    #endregion
  }
}<file_sep>/LibEditSpatial/Model/StaticHelper.cs
﻿using System.Collections.Generic;

namespace LibEditSpatial.Model
{
  public static class StaticHelper
  {
    public static bool GetBool(this Dictionary<string, string> dict, string option)
    {
      if (!dict.ContainsKey(option)) return false;
      switch (dict[option].ToLowerInvariant())
      {
        case "yes":
        case "on":
          return true;
        default:
          return false;
      }
    }

    public static string GetString(this Dictionary<string, string> dict, string option)
    {
      if (!dict.ContainsKey(option)) return "";
      return dict[option];
    }

    public static T Get<T>(this Dictionary<string, string> dict, string option, T defaultValue = default(T))
    {
      if (!dict.ContainsKey(option)) return defaultValue;

      switch (typeof (T).ToString())
      {
        case "System.Boolean":
          return (T) (object) GetBool(dict, option);

        case "System.Int16":
        case "System.Int32":
        case "System.Int64":
        {
          int temp;
          if (int.TryParse(dict[option], out temp))
            return (T) (object) temp;
          break;
        }

        case "System.Single":
        {
          float temp;
          if (float.TryParse(dict[option], out temp))
            return (T) (object) temp;
          break;
        }

        case "System.Double":
        {
          double temp;
          if (double.TryParse(dict[option], out temp))
            return (T) (object) temp;
          break;
        }
        case "System.String":
        {
          return (T) (object) dict[option];
        }
        default:
          return defaultValue;
      }
      return defaultValue;
    }
  }
}<file_sep>/WFEditSpatial/Templates/componentparameters.hh
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set ts=4 sw=2 et sts=2:
#ifndef DUNE_COPASI_DIFFUSIONPARAMETERS_HH
#define DUNE_COPASI_DIFFUSIONPARAMETERS_HH

#include <dune/copasi/utilities/componentparameters.hh>
#include <dune/copasi/utilities/datahelper.hh>

#include "local_operator.hh"

/** 
 * Generic DiffusionParameter for all variables. 
 *
 * It is to be initialized from a config file, with the following parameters: 
 * 
 *  - D    -> double, the diffusion coefficient
 *  - Xmin -> double, boundary condition at left boundary
 *  - Xmax -> double, boundary condition at right boundary
 *  - Ymin -> double, boundary condition at top boundary
 *  - Ymax -> double, boundary condition at bottom boundary
 *
 *  - BCType: int, the type of boundary condition
 *            Dirichlet=1, Neumann=-1, Outflow=-2, None=-3
 * 
 * Additionally the values returned can be overwritten by files. For that set the 
 * parameters below to dmp files. 
 *
 *  - file_bcType, a file containing bctypes for all coordinates
 *  - file_dc, a file containing the diffusion coefficients for the coordinates
 *  - file_neumann, a file containing Neumann boundary values for the coordinates
 *  - file_dirichlet, a file containing Dirichlet boundary values for the coordinates
 *  - file_compartment, a file 1 wherever inside compartment and 0 outside for the coordinates
 */
template<typename GV, typename RF>
class DiffusionParameter :
  public Dune::PDELab::DiffusionMulticomponentInterface<Dune::PDELab::DiffusionParameterTraits<GV, RF>,
  DiffusionParameter<GV, RF> >
{
  enum { dim = GV::Grid::dimension };

public:
  typedef Dune::PDELab::DiffusionParameterTraits<GV, RF> Traits;
  typedef typename Traits::BCType BCType;

  DiffusionParameter(const Dune::ParameterTree & param, const std::string cname)
    : time(0.)
    , Dt(param.sub(cname).template get<RF>("D"))
    , Xmin(param.sub(cname).template get<RF>("Xmin", 0))
    , Xmax(param.sub(cname).template get<RF>("Xmax", 0))
    , Ymin(param.sub(cname).template get<RF>("Ymin", 0))
    , Ymax(param.sub(cname).template get<RF>("Ymax", 0))
    , width(param.sub("Domain").template get<int>("width", 0))
    , height(param.sub("Domain").template get<int>("height", 0))
    , boundarytype(BCType::Neumann)
    , dh_bcType(DataHelper::forFile(param.sub(cname).template get<std::string>("file_bcType", "")))
    , dh_neumann(DataHelper::forFile(param.sub(cname).template get<std::string>("file_neumann", "")))
    , dh_dirichlet(DataHelper::forFile(param.sub(cname).template get<std::string>("file_dirichlet", ""), NearestNeighbor))
    , dh_dc(DataHelper::forFile(param.sub(cname).template get<std::string>("file_dc", "")))
    , dh_compartment(DataHelper::forFile(param.sub(cname).template get<std::string>("file_compartment", ""), NearestNeighbor))

  {
    int bc = param.sub(cname).template get<int>("BCType");
    switch (bc) {
    case 1:
      boundarytype = BCType::Dirichlet;
      break;
    case -1:
      boundarytype = BCType::Neumann;
      break;
    case -2:
      boundarytype = BCType::Outflow;
      break;
    case -3:
      boundarytype = BCType::None;
      break;
    }
  }


  //! tensor permeability
  typename Traits::RangeFieldType
    D(const typename Traits::ElementType& e, const typename Traits::DomainType& x_) const
  {
    typename Traits::DomainType x = e.geometry().global(x_);
    
    if (dh_dc == NULL)
    {
      if (dh_compartment == NULL) return Dt;
      return Dt * dh_compartment->get(double(x[0]), double(x[1]));
    }

    return (typename Traits::RangeFieldType)dh_dc->get(double(x[0]), double(x[1]));
  }

  //! source/reaction term

  typename Traits::RangeFieldType    q(const typename Traits::ElementType& e, const typename Traits::DomainType&) const
  {
    return 0.0;
  }

  //! boundary condition type function
  BCType
    bctype(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x_) const
  {
    if (dh_bcType == NULL) return boundarytype;

    typename Traits::DomainType x = is.geometry().global(x_);
    return (BCType)dh_bcType->get(double(x[0]), double(x[1]));
  }

  bool
    isDirichlet(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x) const
  {
    return bctype(is, x) == BCType::Dirichlet;
  }

  //! Dirichlet boundary condition value
  typename Traits::RangeFieldType
    g(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x_) const
  {
    if (!is.boundary() || bctype(is, x_) != BCType::Dirichlet)
      return 0; // Dirichlet is zero

    typename Traits::DomainType x = is.geometry().global(x_);

    if (dh_dirichlet != NULL)
    {
      return (typename Traits::RangeFieldType)dh_dirichlet->get(double(x[0]), double(x[1]));
    }

    if (x[0] < 1e-6)
      return Xmin;
    if (x[0] > width - 1e-6)
      return Xmax;
    if (x[1] < 1e-6)
      return Ymin;
    if (x[1] > height - 1e-6)
      return Ymax;

    return 0.0;
  }

  //! Neumann boundary condition
  typename Traits::RangeFieldType
    j(const typename Traits::IntersectionType& is, const typename Traits::IntersectionDomainType& x_) const
  {
    if (!is.boundary() || bctype(is, x_) != BCType::Neumann)
      return 0.0;

    typename Traits::DomainType x = is.geometry().global(x_);
    if (dh_neumann != NULL)
    {
      return (typename Traits::RangeFieldType)dh_neumann->get(double(x[0]), double(x[1]));
    }

    if (x[0] < 1e-6)
      return Xmin;
    if (x[0] > width - 1e-6)
      return Xmax;
    if (x[1] < 1e-6)
      return Ymin;
    if (x[1] > height - 1e-6)
      return Ymax;

    return 0.0;
  }

  //! set time for subsequent evaluation
  void setTime(RF t)
  {
    time = t;
  }

  void setTimeTarget(RF time_, RF dt_)
  {
    tend = time_;
  }

  //! to be called once before each time step
  void preStep(RF time_, RF dt_, int stages)
  {

  }


private:
  RF time, tend, dt;
  const RF Dt;
  const RF Xmin;
  const RF Xmax;
  const RF Ymin;
  const RF Ymax;
  const RF width;
  const RF height;
  BCType boundarytype;
  const DataHelper* dh_bcType;
  const DataHelper* dh_neumann;
  const DataHelper* dh_dirichlet;
  const DataHelper* dh_dc;
  const DataHelper* dh_compartment;
};


#endif
<file_sep>/LibEditSpatial/Model/GlobalConfig.cs
using System.Collections.Generic;

namespace LibEditSpatial.Model
{
  public class GlobalConfig
  {
    public bool WriteVTK { get; set; }
    public string VTKname { get; set; }
    public int Overlap { get; set; }
    public int IntegrationOrder { get; set; }
    public int SubSampling { get; set; }
    public string TimeStepping { get; set; }
    public string ExplicitSolver { get; set; }
    public string ImplicitSolver { get; set; }
    public string SBMLFile { get; set; }

    public static GlobalConfig Default
    {
      get
      {
        return new GlobalConfig
        {
          WriteVTK = true,
          ExplicitSolver = "RK4",
          ImplicitSolver = "Alexander2",
          IntegrationOrder = 2,
          SubSampling = 2,
          Overlap = 1,
          TimeStepping = "implicit"
        };
      }
    }

    public static GlobalConfig FromDict(Dictionary<string, string> dict)
    {
      var result = new GlobalConfig
      {
        WriteVTK = dict.GetBool("writeVTK"),
        VTKname = dict.GetString("VTKname"),
        Overlap = dict.Get<int>("overlap"),
        IntegrationOrder = dict.Get<int>("integrationorder"),
        SubSampling = dict.Get<int>("subsampling"),
        TimeStepping = dict.GetString("timestepping"),
        ExplicitSolver = dict.GetString("explicitsolver"),
        ImplicitSolver = dict.GetString("implicitsolver"),
        SBMLFile = dict.GetString("sbmlfile")
      };

      return result;
    }

    public Dictionary<string, string> ToDict()
    {
      var result = new Dictionary<string, string>();
      result["writeVTK"] = WriteVTK ? "yes" : "no";
      result["VTKname"] = VTKname;
      result["overlap"] = Overlap.ToString();
      result["integrationorder"] = IntegrationOrder.ToString();
      result["subsampling"] = SubSampling.ToString();
      result["timestepping"] = TimeStepping;
      result["explicitsolver"] = ExplicitSolver;
      result["implicitsolver"] = ImplicitSolver;
      result["sbmlfile"] = SBMLFile;
      return result;
    }
  }
}<file_sep>/WFEditSpatial/Templates/reactionadapter.h
#ifndef REACTIONADAPTER_H
#define REACTIONADAPTER_H

#include <dune/copasi/utilities/sbmlhelper.hh>

//! Adapter for system of equations
/*!
  \tparam M model type
  This class is used for adapting system of ODE's (right side)
  to system of PDE's (as source term)
  What need to be implemented is only the evaluate function
*/
template<typename RF>
class ReactionAdapter
{
public:

    //! constructor stores reference to the model
    ReactionAdapter(const Dune::ParameterTree & param_)
        : param(param_)
%INITIALIZER%
    {

    }

    void preStep(RF time,RF dt,int stages)
    {

    }

    //! evaluate model in entity eg with local values x
    template<typename EG, typename LFSU, typename X, typename LFSV, typename R, typename DomainType>
    void evaluate(const EG& eg, const LFSU& lfsu, const X& x, const LFSV& lfsv, R& r, const DomainType& x_)
    {
      const auto& pos = eg.geometry().global(x_);

%ODES%
    }

private:
    const Dune::ParameterTree & param;
%DECLARATION%
};

#endif // REACTIONADAPTER_H
<file_sep>/README.md
# Edit Spatial 
This project contains a prototype editor for manipulating models using SBML Models using [SBML Level 3](http://sbml.org/Documents/Specifications) with the [Spatial package](http://sbml.org/Community/Wiki/SBML_Level_3_Proposals/Spatial_Geometries_and_Spatial_Processes "Spatial package"). 

## Using the Editor
There is not much there just yet, basically just open the SBML model in it and inspect / modify the geometry. Currently supported geometries are images and analytic geometries. To make it easier to compose analytic geometries. The following predefined functions can be used: 

* CIRCLE: use like: `CIRCLE(x,y,25,25,10)`
* ELLIPSE: use like: `ELLIPSE(x,y,25,25,10,10)` 
* RECTANGLE: use like: `RECT(x,y,10,10,10,10)` 
* FISH: use like: `FISH(x,y,width,height)`

## How to build
The project is a .NET project simply open and compile `EditSpatial.sln` with either Visual Studio or MonoDevelop. 

## Libraries
This project requires the following libraries: 

- [libSBML](http://sbml.org/Software/libSBML) along with a supported xml library (xml2, expat, xerces-c)
- [Ookii.Dialogs](http://www.ookii.org/) used for browsing folders, easily

## License
This project is licensed under the BSD license: 

```
Copyright (c) 2015-2018, <NAME>  
All rights reserved. 

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are 
met: 

Redistributions of source code must retain the above copyright notice, 
this list of conditions and the following disclaimer. Redistributions in 
binary form must reproduce the above copyright notice, this list of 
conditions and the following disclaimer in the documentation and/or 
other materials provided with the distribution. THIS SOFTWARE IS 
PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

```
<file_sep>/WFEditSpatial/Templates/initial_conditions.h

/** \brief A function for initial values of the model
 */
template<typename GV, typename RF>
class UGeneralInitial
        : public Dune::PDELab::GridFunctionBase<Dune::PDELab::
        GridFunctionTraits<GV, RF, 1, Dune::FieldVector<RF, 1> >, UGeneralInitial<GV, RF> >
{
    const GV& gv;

public:
    typedef Dune::PDELab::GridFunctionTraits<GV,RF,1,Dune::FieldVector<RF,1> > Traits;

    UGeneralInitial(const GV& gv_)
      : gv(gv_)
      , param()
      , mComponentIndex(0)
    {}

    //! construct from grid view
    UGeneralInitial(const GV& gv_, const Dune::ParameterTree & param_, int componentIndex)
        : gv(gv_)
        , param(param_)
        , mComponentIndex(componentIndex)
%INITIALIZER%
    {}

    //! evaluate extended function on element
    inline void evaluate (const typename Traits::ElementType& e,
                          const typename Traits::DomainType& xlocal,
                          typename Traits::RangeType& __initial) const
    {
      const auto& x = e.geometry().global(xlocal);
%ADDITIONAL_INITIAlIZATION%

      switch(mComponentIndex)
      {
%INITIALCONDITION%
        default:
        {
          // this cannot happen
          Dune::PDELab::Exception ex;
          ex.message("Unexpected Index.");
          throw ex;
        }
      }
    }

    //! get a reference to the grid view
    inline const GV& getGridView () {return gv;}

private:

    const Dune::ParameterTree & param;
    int mComponentIndex;
%DECLARATION%
};
<file_sep>/WFEditSpatial/Model/AnalyticSettings.cs
namespace EditSpatial.Model
{
  public class AnalyticSettings
  {
    public string Type { get; set; }
    public string Math { get; set; }
  }
}<file_sep>/WFEditSpatial/Controls/ControlMapCompartments.cs
﻿using EditSpatial.Model;
using libsbmlcs;

namespace EditSpatial.Controls
{
  public partial class ControlMapCompartments : BaseSpatialControl
  {
    public ControlMapCompartments()
    {
      InitializeComponent();
    }

    private libsbmlcs.Model Current { get; set; }

    public void InitializeFrom(libsbmlcs.Model model)
    {
      grid.Rows.Clear();
      Current = model;

      colDomainType.Items.Clear();

      if (model == null) return;

      var mplug = (SpatialModelPlugin) model.getPlugin("spatial");
      if (mplug != null)
      {
        var dtypes = mplug.getGeometry().getListOfDomainTypes();
        for (var i = 0; i < dtypes.size(); ++i)
        {
          var dtype = dtypes.get(i);
          if (dtype == null) continue;
          colDomainType.Items.Add(dtype.getId());
        }
      }


      for (long i = 0; i < model.getNumCompartments(); ++i)
      {
        var comp = model.getCompartment(i);
        var map = comp.getCompartmentMapping();
        grid.Rows.Add(comp.getId(), (map == null) ? "" : map.getDomainType());
      }
    }

    public override void SaveChanges()
    {
      if (Current == null) return;
      for (var i = 0; i < grid.Rows.Count && i < Current.getNumCompartments(); ++i)
      {
        var row = grid.Rows[i];
        var current = Current.getCompartment((string) row.Cells[0].Value);
        var map = current.getCompartmentMapping();
        if (map == null) continue;
        map.setDomainType((string) row.Cells[1].Value);
      }

      OnModelChanged();
    }

    public override void InvalidateSelection()
    {
      Current = null;
      InitializeFrom(null);
    }
  }
}<file_sep>/LibEditSpatial/Dialogs/FormResize.cs
﻿using System.Drawing;
using System.Globalization;
using System.Windows.Forms;

namespace LibEditSpatial.Dialogs
{
  public partial class FormResize : Form
  {

    public RectangleF CanvasBounds
    {
      get { 
        float minX; float.TryParse(txtMinX.Text, out minX);
        float maxX; float.TryParse(txtMaxX.Text, out maxX);
        float minY; float.TryParse(txtMinY.Text, out minY);
        float maxY; float.TryParse(txtMaxY.Text, out maxY);
        return new RectangleF(minX, minY, maxX, maxY);
      }
      set
      {
        txtMinX.Text = value.X.ToString(CultureInfo.InvariantCulture);
        txtMinY.Text = value.Y.ToString(CultureInfo.InvariantCulture);
        txtMaxX.Text = value.Width.ToString(CultureInfo.InvariantCulture);
        txtMaxY.Text = value.Height.ToString(CultureInfo.InvariantCulture);
      }
    }

    public Size Dimensions
    {
      get { 
        int width; int.TryParse(txtColumns.Text, out width);
        int height; int.TryParse(txtRows.Text, out height);
        return new Size(width, height);
      }
      set { 
        txtColumns.Text = value.Width.ToString(); 
        txtRows.Text = value.Height.ToString(); 
      }
    }

    public bool ScaleContents
    {
      get { return chkScaleContents.Checked; }
      set { chkScaleContents.Checked = value; }
    }


    public FormResize()
    {
      InitializeComponent();
    }
  }
}
<file_sep>/LibEditSpatial/Model/VoidDelegate.cs
namespace LibEditSpatial.Model
{
  public delegate void VoidDelegate();
}