v0.14.0
Development rules
  • MoFEM core is to be modular, extendable, and to remain as small as possible to enable its future evolution.
  • MoFEM core is free from implementation of specific physics.
  • MoFEM User modules should present good development practices.
  • MoFEM User modules are examples of implementation for particular physical problems.
  • User modules is specific code, with bespoke solutions, to be used by external users. Solutions can be closed, bespoke, and/or tailored.
  • Code always works in parallel.

General rules

  • If a section of the code does not have to be fast, make it generic.
  • Make generic code as simple as possible.
  • Do not duplicate code with similar functionality.
  • Make code fast. Profile the code to check what needs to be made fast.

Hide complexities.

  • Isolate complexities. You can focus only on one thing.
  • Avoid bool flags and code branching. This makes code difficult to test, debug, and understand.
  • A perfect operator is a black box.
  • Black boxes do not create dependencies, enabling future evolution and development
  • Do not expose members of user data operators. Make members of the function private.
  • Common data for a series of operators are local.
{c++}
template <int DIM, AssemblyType A, IntegrationType I, typename BoundaryEleOp>
MoFEMErrorCode opFactoryBoundaryRhs(
boost::ptr_deque<ForcesAndSourcesCore::UserDataOperator> &pip,
std::string sigma, std::string u, bool is_axisymmetric = false) {
MoFEMFunctionBegin;
using C = ContactIntegrators<BoundaryEleOp>;
auto common_data_ptr = boost::make_shared<ContactOps::CommonData>();
pip.push_back(new OpCalculateVectorFieldValues<DIM>(
u, common_data_ptr->contactDispPtr()));
pip.push_back(new OpCalculateHVecTensorTrace<DIM, BoundaryEleOp>(
sigma, common_data_ptr->contactTractionPtr()));
pip.push_back(
new typename C::template Assembly<A>::template OpConstrainBoundaryRhs<
DIM, GAUSS>(sigma, common_data_ptr, is_axisymmetric));
MoFEMFunctionReturn(0);
}
  • The perfect operator is dimension and element type agnostic.
  • Push operators in a factory function or a lambda function if an ad hoc solution
  • Push operator by operator defined in "cpp" file. Hide implementation.

hpp file:

{c++}
namespace BlackBox {
struct OpBlackBox; // only declaration
OpBlackBox* createOpBlackBox();
}

cpp: file:

{c++}
namespace BlackBox {
struct OpBlackBox: private ForcesAndSourcesCore::UserDataOperator {
private:
// definition
};
OpBlackBox* createOpBlackBox() { return new OpBalckBox(); }
}

Testing

  • Test the code, not the method.
  • Tests are fast.

Avoid dependencies

  • Do not add software dependencies to core library.
  • If you have to add a dependency, make it optional.
  • With more libraries, there is a larger likelihood of the code breaking at compilation.
  • Some dependencies should be restricted to users modules
  • All dependencies are managed managed Spack