基于开源软件的商业仿真软件开发战略


  虽然还存在不少争论,基于开源软件的商业模式今天已经得到大家的认可([1]~[4])。从以前的RedHat到近期的Docker,你可以找到不少成功的范例。但是这些例子集中于网络,操作系统,亦或是文字处理这样的通用性很强的领域,在仿真软件这样的专业性很强的领域,更具体地说如果想在中国开发仿真软件又会如何呢?

   在中国开发仿真软件需要认清如下三个问题

  • 开发仿真软件首先需要了解"应用(研究的/工业的)→物理/力学原理 → 数学+ 算法+ 软件工程"这一系列的过程。如果你要开发一流的软件,你更是需要精通这一系列的过程。根据我个人的经验,最低限度你需要经自己的手推导出全部的公式,厘清每一个数据的走向。这是一个很高的门槛,即要求很高的数理素养,还要具备一定的软件开发人员的工匠气质,决定了能参加这类软件开发的人很少。科学计算平台trilinos可以是最有影响力的平台之一了,它在Github上现在的星数是349,这个数字和高人气的通用开源软件相比要差一,二个数量级。
  • 中国的研究水平较低。在我熟悉的专业(力学有限元)中,我没有看到国内专家的高水平专著,也很少看到有影响力的论文。这是中国很难出现相应的高水平软件的原因之一。另外,据我个人的经验,中国的教授们很少自己编程,因此很难理解认识软件开发的难点,痛点。而据我所知,名著Finite Element Method的作者之一·R.L.Taylor,名著Finite Element Procedure的作者K.J. Bath都是会自己动手编程的,而你如果有机会读到FEAP的源码,你还可以看到英年早逝的力学大师J.C.Simo和其他名家开发的代码。
  • 中国缺少开源软件开发的文化。有句话说"Opensource is an ethos not just license". 在中国,竞价排名,知识付费的盛行正是反映了某种缺失。另外,在欧美,日本,利用税金开发的软件一般是要求开源的,据说中国也投入过数亿开发有限元软件,但是并不公开。如上所述,我们应该可以想象光靠高校等的研究人员来开发出一个可用的软件是非常困难的,最好的可能是开发出一个还不错的基础平台。如果能借此培养一些开发人才,形成一定的开源开发社区,从企业拿到二次,三次开发资金,还有成长起来的可能。上面的独吞模式必定是一条走不通的死胡同!参考这里
  下面是我基于开源软件的商业仿真软件开发战略的看法:

1.  我们必须利用开源软件

   和华为的鸿蒙软件系统不会从零开发的理由类似,我们不应该从零开始开发一个仿真软件。仿真软件的复杂程度也许不如操作系统,但是由于要求较高的数理素养,起点也较高。另外,以有限元为例,经过几十年的发展其算法已经相对成熟。同时伴随开源社区,软件工程思想的发展普及,已经存在有不少设计清晰,算法实现优秀的部品软件。如实现基层算法的blaslapack,图论工具metis zoltan(常用于网格分区和矩阵排序),线性方程组求解器mumps ,固有值求解器arpack等。再重新开发这些功能既无必要,你也很少有开发出功能超出这些千锤百炼的软件的可能。至于完整的FEM开源软件也有不少,比如这里这里。对于仿真软件的开发弱者,合理地利用这些开源软件将会给与我们一个快速追赶的机会。
   大部分的开源软件出自美国,这就是实力和底蕴!历史的经验告诉我们要小心开源软件的法律问题。

2.  开源软件协议
  (我不是法律专家,下面意见仅供参考。如有错误,敬请斧正!)

  • 具有传染性的GPL协议: 基于GPL协议软件开发的软件也必须是GPL协议。GPL要求你在将你的软件贩卖给用户时必须向用户公开你的源代码。如果你的软件只做SaaS服务,倒是可以不公开源代码,因为你并没有把软件贩卖给用户。
  • LGPL协议:  如果只是与LGPL软件做动态链接,那么你的软件不需遵守LGPL协议。
  • BSD/MIT/X11协议:  不要求基于这些协议软件开发的软件公开源码,只要求著作权等的表示
  • Apache协议: 与上述BSD协议类似,但是受美国出口法律约束。(注: 我注意到中国厂家的WPS也有类似条款,因此在法律上华为现在不能使用WPS
  • 在美国的开源服务器,如GitHub也受美国法律约束。这里面是有很多隐患的,需要小心。
3.  基于开源软件的商业战略
         基于开源软件的商业战略有很多。比如在下面的参考文献【1】中就列出了18种。这里有一些是土豪专用,下面我们看一看有些什么基于现有开源软件的商业化途径。

  • Support and service: 为开源软件的使用提供技术服务。RedHat采用的是这种策略。这个是不是中国人最熟悉的做法?我发现中国有不少提供国外商业仿真软件使用方法培训的公司,奇怪的是它们并不能开发软件,甚至没有软件的代理权?开源软件的使用提供技术服务是进一步的做法,由于熟悉开源软件内部,应该能做得更好。不过我怀疑这种模式的收入有限,因为仿真软件的用户数量毕竟不能和RedHat这样的操作系统,LAMP这样的开发环境相比。但是另一方面,软件的咨询服务方面的费用比例正在上升。具体的数字就不知道了。
  • Dual license: 提供限定功能的免费版本和无限定的付费版本。一般情况下免费版本的使用也加上一定条件,如源码修正必须公开,不允许商用等。这种方式营销的面比较强。免费版本可以使潜在的用户产生信任感,得到迅速的反馈,了解用户的需求,还可以得到免费的软件测试,甚至是开发人员。MySQL采用的是这种策略。
  • Constomazation: 付费定制。定制的内容可以是现有软件欠缺的,也有可能是特殊行业的特殊功能,也有可能是便利化工具。以近期流行的3D打印为例,其特点是材料的渐增过程,解法上也许不需要什么新东西,需要的就是计算工具的重组和相应的便利化工具。个人认为这是一个非常广阔的领域。能够熟练地操作这一工具是打破ABAQUS,ANSYS等独霸市场的有力武器。
  • SaaS: Google,eBay他们开发软件,但是不买软件,他们买网上服务。在这里,你用GPL软件也可以,这是其魅力之一。
  • 人才培养: 归根结底,如果没有相应的人才,什么战略都归为空谈。而中国正是缺少这样的人才。如果能够通过开源软件形成一个开发社区,可以用来教育培养相应的开发人才向商业软件公司输送。也许这才是最根本靠谱的战略吧。
   


参考文献
1.  Business models for open-source software
2.  Setting an Open Source Strategy
3.  Open Sources: Voices from the Open Source Revolution
4.  Enterprises Open Source: A Practical Introduction
5.  The Open Source Software Business Model Blueprint

mingw DLL to MSVC libs using cmake

1.  Generate makefile by cmake
     cmake -G "MSYS Makefiles" -DBUILD_SHARED_LIBS=ON

In msys bash do follows
2.  Compile in mingw
      make
which would generate dll library XXX.dll
3.  Generate def file
     gendef XXX.dll

In VS command prompt do follows
4. Generate MSVC library
    lib /machines:x64 /def:XXX.def /out:XXX.lib


A MSVC dll file could also be transfered into mingw .a file
1. Generate def file
    gendef  XXX.dll
2. Generate .a library
    dlltool --dllname XXX.dll --input-def XXX.def --output-lib libXXX.a

FEM as a tool of PDE solver and its open source implementation

1. Finite element function space
    The construction of the finite element space Vh begins with subdividing the domain $\Omega$ into a set  of non-overlapping or overlapping, such as those implemented in GFEM, cells $T_1,T_2,...T_m$. The domain  $\Omega$ can now be approximated with a mesh domain, 
                                $T_h(\Omega)=\cup{T_i}$
The cells are typically simple polygonal shapes like intervals, triangles, quadrilaterals, tetrahedra or hexahedra but also could be described by B-splines as in isogeometric analysis.

    Once a domain Ω has been partitioned into cells, one may define a local function space V on each
cell Ti and use these local function spaces to build the global function space Vh. A cell Ti together
with a local function space V and a set of rules for describing the functions in V is called a finite
element. This definition reads as follows: a finite element is a triple (T, V, L), where
   • the domain T is a bounded, closed subset of $R^d$(for d = 1, 2, 3, . . . ) with nonempty interior and
piecewise smooth boundary;
   • the space V = V(T) is a finite dimensional function space on T of dimension n;
   • the set of degrees of freedom (nodes) L = { 1, 2,..., n} is a basis for the dual space $\hat{V}$; that
is, the space of bounded linear functionals on V.

   If finite element spaces for grad, div, and curl are implemented, most types of PDEs are solvable.

   • H1Space - the most common FE space of continuous, piecewise-polynomial function:
       $H^1(\Omega) = \{v\in L^2(\Omega); \nabla u\in[L^2(\Omega)]^2\}$

   • HCurlSpace - FE space of vector-valued functions discontinuous along mesh edges, with continunous tangential component on the edges 

       $H(curl,\Omega) = \{E\in [L^2(\Omega)]^2; \nabla\times E\in L^2(\Omega)\}$

   • HDivSpace -  FE space of vector-valued functions discontinuous along mesh edges, with continunous normal component on the edges 

       $H(div,\Omega) = \{v\in [L^2(\Omega)]^2; \nabla\cdot v\in L^2(\Omega)\}$


2.  Weak form of PDE
    We consider a general linear variational problem written in the following canonical form: find u ∈ V such that
      $a(u,v)=L(v): \forall v \in \hat{V}$
where the test space $\hat{V}$ is defined by
     $\hat{V}=\{v \in H^1(\Omega):v=0\  on\  \Gamma_\Omega\}$
and the trial space V contains members of Vˆ shifted by the Dirichlet condition:
    $V=\{v \in H^1(\Omega):v=u_0\  on\  \Gamma_\Omega\}$
We thus express the variational problem in terms of a bilinear form a and a linear form (functional) L:
    $a: V \times \hat{V} \to R$
    $L: \hat{V} \to R$
   The basic idea of FEM consists of approximating the big Hilbert space $H_0^1$, e.g., by some appropriate finite-dimensional space $H_h$, where h is a positive parameter, satisfying the following conditions:
   • $H_h \subseteq H_0^1$
   • Solve $a(u_h,v_h)=L(v_h)$ obtain a solution $v_h$ and 
   • $H_h \to H_0^1 \ as \ h \to 0$

Example: Poisson problem
   Find u such that
      $-\Delta u=f, x \in \Omega\subset R^d$
      $u=0 \ on \ \partial\Omega$
After integrating by part, we obtains its weak form
     $Find \ u \in V=H^1_0(\Omega): \{v=0 \ on \ \partial\Omega\}$
     $such \ that \int_\Omega\nabla u\cdot \nabla vd\Omega=\int_\Omega fvd\Omega, \forall v\in V$
where $a(u,v)=\int_\Omega\nabla u\cdot \nabla vd\Omega$, $L(v)=\int_\Omega fvd\Omega$

3. Implementation on some mathmatically oriented open source FEM software
    The general FEM system could be thought of as having form $a(u_h,v_h)=L(v_h)$ and solved by following steps

   • Build a mesh Th. It could be also read from exsting mesh files.
   • Define the finite element space Vh

   • Define the variational problem P $a(u_h,v_h)=L(v_h)$

   • Solve the problem P

   • Postprocess

Lets see how it is really implemented in some open source FEM softwares

   We need prepare following input file:

   mesh  Th=square(10,10)        // generate a mesh Th
   fespace Vh(Th,P1)                 // define FE space Vh on Th by element type P1
   Vh u,v                                    // declear funtion in Vh
   func  f = -1                             // constant function
   func g =0                               // constant function
                                                 // $a(u,v)=\int_\Omega\nabla u\cdot \nabla vd\Omega$
  problem Poisson(u,v) = int2d(Th)(dx(uh)*dx(vh)+dy(uh)*dy(vh))   
            -int2d(f*v)                   // $L(v)=\int_\Omega fvd\Omega$
            +on(C, g)                     // u=g on boundary C
  Poisson                                  // solve it
  plot(u)                                   // visualize solution

3.2 FEniCS
  Following python script is needed:

  from fenics import *                                

  Th = UnitSquareMesh(10, 10)                // generate a mesh Th
  Vh = FunctionSpace(mesh, ’P’, 1)         // define FE space Vh on Th by element type P1
  u = TrialFunction(Vh)                             // declear funtion in Vh
  v = TestFunction(Vh)                             // declear funtion in Vh
  f = Constant(-1.0)                                   // constant function
  g = Constant(0.0)                                    // constant         
  a = dot(grad(u), grad(v))*dx                  // $a(u,v)=\int_\Omega\nabla u\cdot \nabla vd\Omega$     
  L = f*v*dx                                             /$L(v)=\int_\Omega fvd\Omega$
  def boundary(x, on_boundary):
        return on_boundary
  bc = DirichletBC(V, g, boundary)
  solve(a == L, u, bc)                               // solve it

  plot(u)                                                   // visualize solution

 A Julia wrapper of FEniCS given here.

3.3 Feel++
  Following c++ program construct a solver for Poisson equation

auto mesh = ... // build a mesh auto Vh = Pch<a>(mesh) // define FE space auto a = form2(_trial=Vh, _test=Vh) // declare a two form                                                                      //$a(u,v)=\int_\Omega\nabla u\cdot \nabla vd\Omega$ a = integrate( _range=elements(mesh), _expr=gradt(u)*trans(grad(u)); auto l= form1(_test=Vh) //declare a one form  l = integrate( _range=elements(mesh), _expr=f*id(u));   /$L(v)=\int_\Omega fvd\Omega$
a.solve(_solution=u, _rhs=l )                                            // solve it

3.4 GetDP
The input file to solve Poisson equation look like:

FunctionSpace {
    { Name H1; Type Form0;
      BasisFunction {
        { Name sn; NameOfCoef vn; Function BF_Node; Support D; Entity NodesOf[All]; }
      }
    }
  }
  Formulation{
    { Name Poisson; Type FemEquation;
      Quantity {
        { Name v; Type Local; NameOfSpace H1; }
      }
      Equation {
        Integral { [ a[] * Dof{d v}, {d v} ] ; In D; Jacobian V; Integration I; }
        Integral { [ f[], {v} ] ; In D; Jacobian V; Integration I; }
      }
    }
  }
3.5 Sundance As a part of big Trilinos, it seems that it is not on develop right now. Compile error may happen due to its old-styled code. A modified fork is given here.


 // get a mesh Mesh mesh = meshSrc.getMesh(); /* Test and unknown function */
      BasisFamily basis = new Lagrange(1);
      Expr u = new UnknownFunction(basis, "u");
      Expr v = new TestFunction(basis, "v"); /* We need a quadrature rule for doing the integrations */
      QuadratureFamily quad1 = new GaussianQuadrature(1);


      /** Write the weak form */
      Expr eqn
  = Integral(interior, (grad*u)*(grad*v), quad1)
  + Integral(east, v, quad1); /* Set up linear problem */
      LinearProblem prob(mesh, eqn, bc, v, u, vecType);

      /* --- solve the problem --- */
      /* Create the solver as specified by parameters in an XML file */
      LinearSolver<double> solver
 = LinearSolverBuilder::createSolver(solverFile);


      /* Solve! The solution is returned as an Expr containing a
       * DiscreteFunction */
      Expr soln = prob.solve(solver); 3.6 MFEM You cannot define above two form and one form youeself, MFEM provides prepared classes of one form and two form integrators instead. Something like, e.g.

Therefore, it is not that strictly mathematically oriented than above ones but would expect more computational efficiency because software intepretation of one form, two form definition are not needed anymore.
   Its implementation on Poisson problem looks like:

    // read a mesh
     Mesh *mesh = new Mesh(mesh_file, 1, 1);
    // define FE space
     FiniteElementCollection *fec;
     FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
    // define one form
     LinearForm *b = new LinearForm(fespace);
     ConstantCoefficient one(1.0);
     b->AddDomainIntegrator(new DomainLFIntegrator(one));
     b->Assemble();
     // define two form
    BilinearForm *a = new BilinearForm(fespace);
    a->AddDomainIntegrator(new MixedGradGradIntegrator(one));
    a->Assemble();


Reference:
1)   A Logg, K-A Mardel. GN Wells: Automated Solution of Differential Equations by the Finite Element Method, Springer-Verlag. 2012
2)  Eric Sonnendru¨cker, Ahmed Ratnani : Advanced Finite Element Methods

VS Code下cmake, c++编译,调试环境的构成步骤

1   下载必须extension      按[Ctrl+Shift+X]打开extension窗口,选择安装"C/C++", "CMake", "CMake Tools" 2   在VSCode下打开作业目录 ...