diff --git a/CMakeLists.txt b/CMakeLists.txt index f3c4d5e..5ed929e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,8 @@ set(SOURCES navier_stokes_base.cpp incompressible/incompressible_navier_stokes_base.cpp incompressible/fv1/navier_stokes_fv1.cpp - incompressible/fe/navier_stokes_fe.cpp + incompressible/fv1/navier_stokes_fv1_cutElem.cpp + incompressible/fe/navier_stokes_fe.cpp incompressible/fvcr/navier_stokes_fvcr.cpp incompressible/fv/navier_stokes_fv.cpp @@ -74,7 +75,8 @@ set(SOURCES navier_stokes_base.cpp incompressible/fvcr/register_fvcr.cpp incompressible/fe/register_fe.cpp - incompressible/incompressible_navier_stokes_plugin.cpp) + incompressible/incompressible_navier_stokes_plugin.cpp + incompressible/particle_laden_flow_plugin.cpp) ################################################################################ diff --git a/incompressible/CMakeLists.txt b/incompressible/CMakeLists.txt index 3a4c3f5..c5c1450 100644 --- a/incompressible/CMakeLists.txt +++ b/incompressible/CMakeLists.txt @@ -51,6 +51,7 @@ set(SOURCES ../navier_stokes_base.cpp incompressible_navier_stokes_base.cpp fv1/navier_stokes_fv1.cpp + fv1/navier_stokes_fv1_cutElem.cpp fe/navier_stokes_fe.cpp fvcr/navier_stokes_fvcr.cpp fv/navier_stokes_fv.cpp diff --git a/incompressible/fv1/bnd/inflow_fv1_cutElem.h b/incompressible/fv1/bnd/inflow_fv1_cutElem.h new file mode 100644 index 0000000..f62233a --- /dev/null +++ b/incompressible/fv1/bnd/inflow_fv1_cutElem.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013: G-CSC, Goethe University Frankfurt + * Author: Andreas Vogel + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#ifndef __H__UG__NAVIER_STOKES__INCOMPRESSIBLE__FV1__BND__INFLOW_FV1_CUT_ELEM__ +#define __H__UG__NAVIER_STOKES__INCOMPRESSIBLE__FV1__BND__INFLOW_FV1_CUT_ELEM__ + +#include "../../bnd/inflow_base.h" +#include "../navier_stokes_fv1_cutElem.h" + +#include "lib_disc/spatial_disc/elem_disc/neumann_boundary/neumann_boundary_base.h" +#include "lib_disc/spatial_disc/constraints/dirichlet_boundary/lagrange_dirichlet_boundary.h" +#include "lib_disc/spatial_disc/elem_disc/neumann_boundary/fv1/neumann_boundary_fv1.h" + +namespace ug{ +namespace NavierStokes{ + +template < typename TDomain, typename TAlgebra> +class NavierStokesInflowFV1_cutElem + : public NavierStokesInflowBase +{ + private: + static const int dim = TDomain::dim; + + public: + + /// returns the number of element discs + virtual size_t num_elem_disc() const {return 1;} + + /// returns the element disc + virtual SmartPtr > elem_disc(size_t i) {return m_spNeumannDisc;} + + /// returns the number of constraints + virtual size_t num_constraint() const {return 1;} + + /// returns an element disc + virtual SmartPtr > constraint(size_t i) {return m_spDirichletConstraint;} + + public: + /// Constructor + NavierStokesInflowFV1_cutElem(SmartPtr< NavierStokesFV1_cutElem > spMaster) + : m_spNeumannDisc(new NeumannBoundaryFV1(spMaster->symb_fcts()[dim].c_str())), + m_spDirichletConstraint(new DirichletBoundary), + m_spMaster(spMaster) // copy constructor not defined for _cutElem? + { + const std::vector& vFctName = m_spMaster->symb_fcts(); + + if(vFctName.size() != TDomain::dim + 1) + UG_THROW("NavierStokesInflow::set_functions: This Boundary " + "Condition works on exactly dim+1 (velocity+pressure) " + "components, but "<, dim> > user, const char* subsetsBND); + + protected: + /// neumann disc for pressure equation + SmartPtr > m_spNeumannDisc; + + /// dirichlet disc for velocity components + SmartPtr > m_spDirichletConstraint; + + /// The master discretization: + SmartPtr< NavierStokesFV1_cutElem > m_spMaster; +}; + +} // namespace NavierStokes +} // end namespace ug + +#include "inflow_fv1_cutElem_impl.h" + +#endif /* __H__UG__NAVIER_STOKES__INCOMPRESSIBLE__FV1__BND__INFLOW_FV1_CUT_ELEM__ */ diff --git a/incompressible/fv1/bnd/inflow_fv1_cutElem_impl.h b/incompressible/fv1/bnd/inflow_fv1_cutElem_impl.h new file mode 100644 index 0000000..21e3132 --- /dev/null +++ b/incompressible/fv1/bnd/inflow_fv1_cutElem_impl.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013: G-CSC, Goethe University Frankfurt + * Author: Andreas Vogel + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#ifndef __H__UG__NAVIER_STOKES__BND__INFLOW_FV1_CUT_ELEM_IMPL__ +#define __H__UG__NAVIER_STOKES__BND__INFLOW_FV1_CUT_ELEM_IMPL__ + +#include "inflow_fv1_cutElem.h" +#include "lib_disc/spatial_disc/elem_disc/neumann_boundary/fv1/neumann_boundary_fv1.h" + +namespace ug{ +namespace NavierStokes{ +/* +template +NavierStokesInflowFV1_cutElem:: +NavierStokesInflowFV1_cutElem(SmartPtr< NavierStokesFV1_cutElem > spMaster) + : m_spNeumannDisc(new NeumannBoundaryFV1(spMaster->symb_fcts()[dim].c_str())), + m_spDirichletConstraint(new DirichletBoundary), + m_spMaster(spMaster) +{ + const std::vector& vFctName = m_spMaster->symb_fcts(); + + if(vFctName.size() != TDomain::dim + 1) + UG_THROW("NavierStokesInflow::set_functions: This Boundary " + "Condition works on exactly dim+1 (velocity+pressure) " + "components, but "< +void NavierStokesInflowFV1_cutElem:: +add(SmartPtr, dim> > user, const char* subsetsBND) +{ + const std::vector& vFctName = m_spMaster->symb_fcts(); + if(vFctName.empty()) + UG_THROW("NavierStokesInflow::add: Symbolic names for" + " velocity and pressure not set. Please set them first."); + + std::string innerSubsets; + for(size_t s = 0; s < m_spMaster->symb_subsets().size(); ++s){ + if(s > 0) innerSubsets.append(","); + innerSubsets.append(m_spMaster->symb_subsets()[s]); + } + + + m_spNeumannDisc->add(user, subsetsBND, innerSubsets.c_str()); + + std::string velNames; + for(int i=0;i0) velNames.append(","); + velNames.append(vFctName[i]); + } + m_spDirichletConstraint->add(user.template cast_dynamic, dim> >(), velNames.c_str(), subsetsBND); +} + +} // namespace NavierStokes +} // end namespace ug + +#endif /* __H__UG__NAVIER_STOKES__BND__INFLOW_FV1_CUT_ELEM_IMPL__ */ diff --git a/incompressible/fv1/moving_particle/Info File 1 b/incompressible/fv1/moving_particle/Info File 1 new file mode 100644 index 0000000..7704b16 --- /dev/null +++ b/incompressible/fv1/moving_particle/Info File 1 @@ -0,0 +1,40 @@ +/* Info File for 'MovingParticle' class: */ + +The 'MovingParticle' class enables the simulation of moving particles in a fluid. Detailed descriptions of the mathematical theory and numerical simulations can be found in [1] and [2]. +The interaction between the fluid and the particles takes place at the interface of the particles, which acts therefore as an immersed interface for the fluid. Consequently, the 'MovingParticle' class implements a derivation of the 'ImmersedInterface' class. It therefore contains all the components of an 'ImmersedInterface' class implemented according to the necessities for moving particles. Those are explained in more detail in the according class types. + + + +The special requirements for a moving particle immersed interface implementation are the following: + +(1) Each particle is described as a rigid motion, i.e. by its two components: the translational and rotational velocity. As a consequence, in the discrete scheme each particle is described by the DoFs for its translational and rotational velocities. Those can be stored in two geometrical nodes of the mesh. Their location within each particle gets defined during an initialisation phase. Most of the nodes within the particles are therefore NO DoFs for the particle, beside the designated nodes (named 'transInd' and 'rotInd' for the associated global indices). + +(2) Furthermore, a particle usually covers more than one (local) finite element. Since the solution for the particle velocities is coupled with and need to be known for all surrounding fluid DoFs, the DoFs need to be accessible on a GLOBAL scale as well. The assigned global indices ('transInd' and 'rotInd') for the particle DoFs need to be accessible globally. Furthermore, the initialization of the interfaces of the particles inherits the collection of all cut elements. + + +The two central methods called during the initialisation (called via the 'init()' of the associated 'CutElementHandler') are: + +---> update_interface_data(): computes all cut elements +---> update_global_indices(): defines the nodes and indices within a particle, which act as particle DoFs + + +Further remarks: + +- Most of the nodes within the particles are NO DoFs for the particle. + +- During the movement of a particle in time, nodes that were part of the particle can get part of the fluid in the next time step. Since they were no designated DoFs within the particle they similarly did not contain the solution. A 'filling' of these so called 'freed' nodes need to be performed during the (geometrical) update of the particle interfaces. + +- During the local assembling of the defect, the solution for the translational or rotational components of the particle velocity will be overwritten with the physical particle velocity (a combination of boths). Before and after these computations the particle solution needs to be transfered back and forth between local and global data structures for all later assembling. + + + + +[1] Hoellbacher S., Wittum G.: + "Rotational test spaces for a fully-implicit FVM and FEM for the DNS of fluid-particle interaction" + Journal of Computational Physics, vol. 393, (2019), pp. 186–213 + open access link: https://authors.elsevier.com/sd/article/S0021999119303298 + DOI: https://doi.org/10.1016/j.jcp.2019.05.004 + +[2] Hoellbacher S., Wittum G.: + "Gradient-consistent enrichment of finite element spaces for the DNS of fluid-particle interaction." + submitted to Jounal of Computational Physics diff --git a/incompressible/fv1/moving_particle/Info File 2 b/incompressible/fv1/moving_particle/Info File 2 new file mode 100644 index 0000000..fd54cbe --- /dev/null +++ b/incompressible/fv1/moving_particle/Info File 2 @@ -0,0 +1,42 @@ +/* Info File for 'ParticleMapper' class: */ + +The 'ParticleMapper' class handles the mapping from the locally computed data (stiffnes matrix, defect) to the global algebra. + +The 2 main components are: + +(1) Mapping of the local indices, which are associated to the particle DoFs to their global indices. +(2) Performing the cross product multiplication for local indices associated to the rotational velocity DoFs. + + +Important methods: + +---> the hierarchy + + 'add_local_mat_to_global()' ---> 'add_local_mat_to_global_interface()' ---> 'add_local_mat_to_global_FT()' + + forwards the adapted mapping for the cut elements + +---> modify_LocalData(): resizes the local data (LocalMatrix or LocalVecor) BEFORE starting the assembling on the element. It is called during the elements discretisation loop (see elem_disc_assemble_util()). + +---> modify_GlobalSol(): writes the particle velocities stored in its designated global indices to own data in order to protect it from being overwritting in the following local assembling process and provide global access. + + + + +The flat-top ansatz space: + +On a cut element, local couplings will be computed for all the intersection points of the interface with the edges of the cut element. The mapping of these couplings to its associated global indices finally defines the property of the underlying finite element space: + +Case 1: Each coupling is mapped to its own, designated global index, i.e. DoF. The resulting space will be the usual finite element space w.r.t. the interface adaped grid. It therefore contains additional nodes along the interface and (virtually) consists of cut elements and original elements. + +Case 2: The local coupling of an intersection point is mapped onto the global index of the original node, which lies accross the interface, but on the corner of the same element. The resulting finite element space will potentially be smaller than the one of the cut element, since two and more (in 3d) intersection points can share the same node accross the interface. The resulting space is a so called "flat top" space, since it results in piecewise constant solutions along the connecting hyperplane between these intersection points. + + +The 'ParticleMapper' class is of type case 2: +It maps all couplings of intersection points to the same translational (and in analogy to the rotational) global index. Therefore, the 'ParticleMapper' is a flat-top mapper. + +In contrast, the 'DiffusionInterfaceMapper' is of type case 1: +In particular, it is a tow-sided mapper and the resulting space is an interface adapted space. + + + diff --git a/incompressible/fv1/moving_particle/Info File 3 b/incompressible/fv1/moving_particle/Info File 3 new file mode 100644 index 0000000..ec52e24 --- /dev/null +++ b/incompressible/fv1/moving_particle/Info File 3 @@ -0,0 +1,6 @@ +/* Info File for 'ParticleBndCond' class: */ + +Assemlbes the stresses on the particle interface. For that, the common forces will be assembled as for usual boundary faces, BUT on the according boundary faces which were computed for the cut element during the local update of the element. + +The boundary faces of the cut elements got stored into the 'm_vBF' member of the 'InterfaceHandlerLocalParticle' class. + diff --git a/incompressible/fv1/moving_particle/immersed_bnd_cond_particle.h b/incompressible/fv1/moving_particle/immersed_bnd_cond_particle.h new file mode 100644 index 0000000..671f81d --- /dev/null +++ b/incompressible/fv1/moving_particle/immersed_bnd_cond_particle.h @@ -0,0 +1,165 @@ +/* + * particle_bnd_cond.h + * + * Created on: 30.01.2015 + * Author: suze + */ + +#ifndef PARTICLE_BND_COND_H_ +#define PARTICLE_BND_COND_H_ + +#ifdef UG_PARALLEL + #include "lib_grid/parallelization/load_balancer_util.h" +#endif + +#include "../../incompressible_navier_stokes_base.h" + + +#include "lib_disc/spatial_disc/immersed_util/immersed_interface_base.h" + +namespace ug{ +namespace NavierStokes{ + +template +class ParticleBndCond : public IInterfaceBndCond +{ + public: + /// World dimension + static const int dim = TDomain::dim; + + /// abbreviation for pressure + static const size_t _P_ = dim; + + /// used boundary face type + typedef typename DimFV1FTGeometry >::BF interfaceBF; + + /// call base class constructor + + ParticleBndCond(SmartPtr > spMaster, + SmartPtr > localHandler); + + + /// destructor + virtual ~ParticleBndCond(){}; + + /////////////////////////////////////////////////////////////////////////////// + /// + /// base class methods + /// + /////////////////////////////////////////////////////////////////////////////// + + public: + /// type of trial space for each function used + void prepare_setting(const std::vector& vLfeID, bool bNonRegularGrid); + + /// prepares the element loop + template + void prep_elem_loop(const ReferenceObjectID roid, const int si){ + } + + /// prepares the element for evaluation + template + void prep_elem(const LocalVector& u, GridObject* elem, + const ReferenceObjectID roid, const MathVector vCornerCoords[]); + + /// finishes the element loop + template + void fsh_elem_loop(){ } + + /// adds the stiffness part to the local jacobian + template + void add_jac_A_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, + const MathVector vCornerCoords[]); + + /// helper method called by 'add_jac_A_elem()' + void add_jac_A_elem_Quadri_for2(LocalMatrix& J, const LocalVector locU); + + /// adds the stiffness part to the local defect + template + void add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, + const MathVector vCornerCoords[]); + + /// helper method called by 'add_def_A_elem()' + void add_def_A_elem_Quadri_for2(LocalVector& locD, const LocalVector locU); + void add_quadri_to_defect(LocalVector& d, const LocalVector& quadriD); + + template + void add_jac_M_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, + const MathVector vCornerCoords[]); + + template + void add_def_M_elem(LocalVector& d, const LocalVector& u, GridObject* elem, + const MathVector vCornerCoords[]); + + template + void add_rhs_elem(LocalVector& d, GridObject* elem, const MathVector vCornerCoords[]); + + + // computes contributions to local stiffness matrix due to the stresses on the particle boundary + void diffusive_flux_Jac(const size_t ip, const interfaceBF& bf, LocalMatrix& J, const LocalVector& u); + void diffusive_flux_Jac_for2(const size_t ip, const interfaceBF& bf, LocalMatrix& J, const LocalVector& u); + void diffusive_flux_Jac_rot(const size_t ip, const interfaceBF& bf, LocalMatrix& J, const LocalVector& u, number importDensity); + + // computes contributions to local defect due to the stresses on the particle boundary + void diffusive_flux_defect(const size_t ip, const interfaceBF& bf, LocalVector& d, const LocalVector& u); + void diffusive_flux_defect_rot(const size_t ip, const interfaceBF& bf, LocalVector& d, const LocalVector& u); + + // reset entries for the momentum equations for interface-corner: + // set entries in local data to zero BEFORE adding the computed contributions from the boundary condition + void remove_equations(LocalVector& d, std::vector vFctID, std::vector vDofID); + void remove_equations(LocalMatrix& J, std::vector vFctID, std::vector vDofID); + + // gets the current index of the particle, which cuts the current element; + // stored in 'InterfaceHandlerLocalParticle' + int get_prtIndex() { return m_spInterfaceHandlerLocal->get_prtIndex(); } + + number get_density(int prtIndex) { return m_spInterfaceHandlerLocal->get_density(prtIndex); } + number get_density_fluid() { return m_spInterfaceHandlerLocal->get_density_fluid(); } + number get_kinVisc_fluid() { return m_spInterfaceHandlerLocal->get_kinVisc_fluid(); } + + // method called by 'ParticleMapper' class for acces to boundary conditions, assembled by this class + void copy_local_couplings_jac() + { m_spInterfaceHandlerLocal->set_local_couplings_jac(rotJ_ind, rotJ_rot); } + void copy_local_couplings_def() + { m_spInterfaceHandlerLocal->set_local_couplings_def(rotD); } + + void write_QuadriSol(const LocalVector origU); + + /// remaps entries for Quadri+Tri-combination of CUT_BY_2_INTERFACE element + size_t remap_for2(size_t dof); + + + /////////////////////////////////////////////////////////////////////////////// + /// class member + /////////////////////////////////////////////////////////////////////////////// + + protected: + // master element disc + SmartPtr > m_spMaster; + + // member from base class + SmartPtr > m_spInterfaceHandlerLocal; + + // local data for assembling: + LocalMatrix rotJ_ind; + LocalMatrix rotJ_rot; + LocalVector rotD; + + + protected: + void register_all(bool bHang); + template + void register_func(); + +}; + + + +} // end namespace NavierStokes +} // end namespace ug + +#include "immersed_bnd_cond_particle_impl.h" + + + +#endif /* PARTICLE_BND_COND_H_ */ diff --git a/incompressible/fv1/moving_particle/immersed_bnd_cond_particle_impl.h b/incompressible/fv1/moving_particle/immersed_bnd_cond_particle_impl.h new file mode 100644 index 0000000..13ec95b --- /dev/null +++ b/incompressible/fv1/moving_particle/immersed_bnd_cond_particle_impl.h @@ -0,0 +1,1173 @@ +/* + * particle_bnd_cond_impl.h + * + * Created on: 30.01.2015 + * Author: suze + */ + +#ifndef PARTICLE_BND_COND_IMPL_H_ +#define PARTICLE_BND_COND_IMPL_H_ + + + +namespace ug{ +namespace NavierStokes{ + +//////////////////////////////////////////////////////////////////////////////// +// Constructor - set default values +//////////////////////////////////////////////////////////////////////////////// +// see 'no_normal_stress_outflow.cpp': +template +ParticleBndCond:: +ParticleBndCond(SmartPtr > spMaster, + SmartPtr > localHandler) + : IInterfaceBndCond(spMaster->symb_fcts(), spMaster->symb_subsets(), localHandler), + m_spMaster(spMaster), + m_spInterfaceHandlerLocal(localHandler) +{ +// update assemble functions + register_all(false); +} + +template +void ParticleBndCond:: +prepare_setting(const std::vector& vLfeID, bool bNonRegularGrid) +{ + // do nothing... +} + +// see 'NavierStokesNoNormalStressOutflowFV1::prep_elem()' +template +template +void ParticleBndCond:: +prep_elem(const LocalVector& u, GridObject* elem, const ReferenceObjectID roid, const MathVector vCornerCoords[]) +{ + +// Update Geometry for this element + static TFVGeom& geo = GeomProvider::get(LFEID(LFEID::LAGRANGE, dim, 1), 1); + + geo.update(elem, vCornerCoords, &(this->subset_handler())); + +} + + +//////////////////////////////////////////////////////////////////////////////// +// methods for adapting jacobian due to the bnd cond +//////////////////////////////////////////////////////////////////////////////// + +// removes the equations with IDs in 'vFctID' locally assembled in the corners of 'vDofID' +template +void ParticleBndCond:: +remove_equations(LocalMatrix& J, std::vector vFctID, std::vector vDofID) +{ + + for(size_t i = 0; i < vDofID.size(); ++i) + for(size_t j = 0; j < vFctID.size(); ++j) + for(size_t fct = 0; fct < J.num_all_row_fct(); ++fct) + for(size_t dof = 0; dof < J.num_all_row_dof(fct); ++dof) + J(vFctID[j], vDofID[i], fct, dof) = 0.0; + +} + + +// see 'NavierStokesNoNormalStressOutflowFV1::diffusive_flux_Jac()' +template +void ParticleBndCond:: +diffusive_flux_Jac_rot(const size_t ip, const interfaceBF& bf, LocalMatrix& J, const LocalVector& u, number importDensity) +{ + + MathMatrix diffFlux, tang_diffFlux; + MathVector normalStress; + MathMatrix RotIndMat, RotRotMat; + MathVector angularVel; + RotIndMat = 0.0; RotRotMat = 0.0; angularVel = 0.0; + + MathMatrix rotationMatIP_transposed = m_spInterfaceHandlerLocal->get_rotationMat(m_spInterfaceHandlerLocal->radial_at_ip(bf.node_id())); + Transpose(rotationMatIP_transposed); + + + for(size_t sh = 0; sh < bf.num_sh(); ++sh) // loop shape functions + { + // 1. Compute the total flux + // - add \nabla u *n + MatSet (diffFlux, 0); + MatDiagSet (diffFlux, VecDot (bf.global_grad(sh), bf.normal())); + + // - add (\nabla u)^T*n + if( !m_spMaster->laplace()) + for (size_t d1 = 0; d1 < (size_t)dim; ++d1) + for (size_t d2 = 0; d2 < (size_t)dim; ++d2) + diffFlux(d1,d2) += bf.global_grad(sh)[d1] * bf.normal()[d2]; + + // Scale by viscosity + diffFlux *= get_kinVisc_fluid(); + + // 2. compute r x [\sigma(u)]*n + // => compute local couplings between rotation and translation/fluid + MatMultiply(RotIndMat, rotationMatIP_transposed, diffFlux); + + // 3. compute r x [\sigma(w x r)]*n + if ( m_spInterfaceHandlerLocal->lies_onInterface(sh) ) + { + // 3.1 compute r x [...]*n + MatMultiply(RotRotMat, rotationMatIP_transposed, diffFlux); + + // 3.2 compute [\sigma(w x r)]*n + MathMatrix rotationMatSH = m_spInterfaceHandlerLocal->get_rotationMat(m_spInterfaceHandlerLocal->radial_at_co(sh)); + MatMultiply(RotRotMat, RotRotMat, rotationMatSH); + + // 3.3 compute (w x r)*n for continuity equation + // transpose in order to multiply from left: + // n^T*rotationMatSH = rotationMatSH^T*n + Transpose(rotationMatSH); + MatVecMult(angularVel, rotationMatSH, bf.normal()); + } + + // 4. Change sign, since force acts onto particle in inverse direction: + //diffFlux *= -1.0; + + // 5. Add flux to local Jacobian + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + for(size_t d2 = 0; d2 < (size_t)dim; ++d2) + { + // write transJ: + J(d1, bf.node_id(), d2, sh) += diffFlux(d1, d2); + rotJ_ind(d1, bf.node_id(), d2, sh) += RotIndMat(d1, d2); + // write rotJ: + rotJ_rot(d1, bf.node_id(), d2, sh) += RotRotMat(d1, d2); + + } + + // 6. Add pressure term to local Jacobian + // 6.1. ----> r x p*n wird NICHT assembliert! + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + J(d1, bf.node_id(), _P_, sh) -= bf.shape(sh) * bf.normal()[d1]; + + + // 7. The continuity equation + VecScale(angularVel, angularVel, bf.shape(sh)); + for (size_t d2 = 0; d2 < (size_t)dim; ++d2) + rotJ_ind(_P_, bf.node_id(), d2, sh) += angularVel[d2]; + } + + +} + + +// see 'NavierStokesNoNormalStressOutflowFV1::diffusive_flux_Jac()' +template +void ParticleBndCond:: +diffusive_flux_Jac(const size_t ip, const interfaceBF& bf, LocalMatrix& J, const LocalVector& u) +{ + + MathMatrix diffFlux, tang_diffFlux; + MathVector normalStress; + + for(size_t sh = 0; sh < bf.num_sh(); ++sh) // loop shape functions + { + // 1. Compute the total flux + // - add \nabla u + MatSet (diffFlux, 0); + MatDiagSet (diffFlux, VecDot (bf.global_grad(sh), bf.normal())); + + + // - add (\nabla u)^T + if( !m_spMaster->laplace()) + for (size_t d1 = 0; d1 < (size_t)dim; ++d1) + for (size_t d2 = 0; d2 < (size_t)dim; ++d2) + diffFlux(d1,d2) += bf.global_grad(sh)[d1] * bf.normal()[d2]; + + // 2. Scale by viscosity + diffFlux *= get_kinVisc_fluid(); + + // 3. Change sign, since force acts onto particle in inverse direction: + //diffFlux *= -1.0; + + // 4. Add flux to local Jacobian + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + for(size_t d2 = 0; d2 < (size_t)dim; ++d2){ + J(d1, bf.node_id(), d2, sh) += diffFlux (d1, d2); + } + + // 5. Add pressure term to local Jacobian + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + J(d1, bf.node_id(), _P_, sh) -= bf.shape(sh) * bf.normal()[d1]; + + } + +} + +template +void ParticleBndCond:: +add_jac_A_elem_Quadri_for2(LocalMatrix& J, const LocalVector locU) +{ + +// Loop the boundary faces to assemble impulse equations + SmartPtr > fluidDensity = m_spMaster->density(); + std::vector& vBF = m_spInterfaceHandlerLocal->get_boundary_faces(); + + if ( vBF.size() != 4 ) + UG_THROW("in 'ParticleBndCond::add_jac_A_elem_Quadri_for2(): vBF.size() should be 4 but is " + << vBF.size() << "\n"); + +// get density + number importDensity = fluidDensity->value(0, 0); + if ( fluidDensity->value(0, 0) != fluidDensity->value(1, 0) ) + UG_THROW("ParticleBndCond::add_jac_A_elem_Quadri_for2(): density different for series 0 and 1: " + << fluidDensity->value(0, 0) << " = " << fluidDensity->value(1, 0) << "\n"); + if ( importDensity != get_density_fluid() ) + UG_THROW("ParticleBndCond::add_jac_A_elem_Quadri_for2(): importDensity = " << importDensity << " = " + "fluidDensity = " << get_density_fluid() << "\n"); + +// loop all boundary faces + for(size_t ip = 0; ip < vBF.size(); ++ip) + { + interfaceBF bf = vBF[ip]; + + // Momentum equation: + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + diffusive_flux_Jac(ip, bf, J, locU); + + + // The continuity equation + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + { + for(size_t sh = 0; sh < bf.num_sh(); ++sh) // loop shape functions + for (size_t d2 = 0; d2 < (size_t)dim; ++d2) + J(_P_, bf.node_id(), d2, sh) += bf.shape(sh) * bf.normal()[d2] + * importDensity; + } + + } // end vBF-loop + +} + + +void copy_and_reset(LocalMatrix& cpJ, LocalMatrix& J) +{ + + for(size_t fct1 = 0; fct1 < J.num_all_row_fct(); ++fct1) + for(size_t dof1 = 0; dof1 < J.num_all_row_dof(fct1); ++dof1) + for(size_t fct2 = 0; fct2 < J.num_all_row_fct(); ++fct2) + for(size_t dof2 = 0; dof2 < J.num_all_row_dof(fct2); ++dof2) + { + cpJ(fct1, dof1, fct2, dof2) = J(fct1, dof1, fct2, dof2); + J(fct1, dof1, fct2, dof2) = 0.0; + } +} + +template +size_t ParticleBndCond:: +remap_for2( size_t dof) +{ + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + if ( dof == m_spInterfaceHandlerLocal->m_vNOInterfaceID[0] ) + return 4; + + if ( m_spInterfaceHandlerLocal->m_vInterfaceID[0] == m_spInterfaceHandlerLocal->m_vQuadriOrigID[0] ) + { + if ( dof == m_spInterfaceHandlerLocal->m_vInterfaceID[0] ) + return 0; + else if ( dof == m_spInterfaceHandlerLocal->m_vInterfaceID[1] ) + return 2; + else UG_THROW("ParticleBndCond::remap: error1: dof = " << dof << "\n"); + } + else if ( m_spInterfaceHandlerLocal->m_vInterfaceID[0] == m_spInterfaceHandlerLocal->m_vQuadriOrigID[2] ) + { + if ( dof == m_spInterfaceHandlerLocal->m_vInterfaceID[0] ) + return 2; + else if ( dof == m_spInterfaceHandlerLocal->m_vInterfaceID[1] ) + return 0; + else UG_THROW("ParticleBndCond::remap: error2: dof = " << dof << "\n"); + } + else UG_THROW("ParticleBndCond::remap: error3: dof = " << dof << "\n"); + +} + +template +template +void ParticleBndCond:: +add_jac_A_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ + ElementModus elemModus = m_spInterfaceHandlerLocal->get_element_modus(elem); + // --> computed via 'compute_element_modus()' during 'update_interface_data()' + +//////////////////////////////////////////////////////////////////////////////// +// (A) Remove local impuls equations for interface-corners +//////////////////////////////////////////////////////////////////////////////// + + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + { + + if ( elemModus != INSIDE_DOM ) + { + // all impulse equations will be removed + std::vector vFctID(dim); + for(size_t i = 0; i < dim; ++i) vFctID[i] = i; + + // equations for interface nodes will be removed + std::vector vDoFID; vDoFID.clear(); + for(size_t i = 0; i < m_spInterfaceHandlerLocal->m_vInterfaceID.size(); ++i) + { + size_t interfaceID = m_spInterfaceHandlerLocal->m_vInterfaceID[i]; + size_t indexToRemove = interfaceID; //m_spInterfaceHandlerLocal->m_vOriginalCornerID[interfaceID]; + vDoFID.push_back(indexToRemove); + } + + remove_equations(J, vFctID, vDoFID); + } + + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// (B) If CUT_BY_2_INTERFACE or StdFVAssembling, no additional interface stresses will be computed +// => map existing entries +//////////////////////////////////////////////////////////////////////////////// + + if ( elemModus == CUT_BY_2_INTERFACE && ! m_spInterfaceHandlerLocal->StdFV_assembling() ) + { + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + { + size_t copyID = m_spInterfaceHandlerLocal->m_vNOInterfaceID[0]; + const LocalIndices& ind = m_spInterfaceHandlerLocal->get_local_indices(); + LocalMatrix buffJ; buffJ.resize(ind); buffJ = 0; + + copy_and_reset(buffJ,J); + + + // remap impulse equation for inside node + for(size_t j = 0; j < dim; ++j) + for(size_t fct = 0; fct < J.num_all_row_fct(); ++fct) + for(size_t dof = 0; dof < 3; ++dof) + J(j, remap_for2(copyID), fct, remap_for2(dof)) = buffJ(j, copyID, fct, dof); + + + // remap pressure equation for all nodes + for(size_t dof1 = 0; dof1 < 3; ++dof1) + for(size_t fct = 0; fct < J.num_all_row_fct(); ++fct) + for(size_t dof2 = 0; dof2 < 3; ++dof2) + J(_P_, remap_for2(dof1), fct, remap_for2(dof2)) = buffJ(_P_, dof1, fct, dof2); + } + + // case == 4: all entries (also for pressure eq) will be written newly, since only boundary faces are relevant + // => during remove_equations() only velocity equations were removed! + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 4 ) + J = 0.0; + + + const LocalIndices& ind = m_spInterfaceHandlerLocal->get_local_indices(); + LocalVector quadriU; + quadriU.resize(ind); + + write_QuadriSol(quadriU); + + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 4 || m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + add_jac_A_elem_Quadri_for2(J, quadriU); + else + UG_THROW("in ParticleBndCond::add_def_A_elem(): wrong amount of interface.size() for CUT_BY_2_INTERFACE: " << m_spInterfaceHandlerLocal->interface_id_all().size() << " ( should be 2!)\n"); + + return; + } + +//////////////////////////////////////////////////////////////////////////////// +// (C) For CUT_BY_INTERFACE: compute stresses on inner boundary faces 'vBF' +// via 'diffusive_flux_Jac()' and add the to local stiffness matrix +//////////////////////////////////////////////////////////////////////////////// + +// Loop the boundary faces for new impuls equations +// --> IFF INSIDE_DOM: vBF.size() = 0 ;-) + SmartPtr > fluidDensity = m_spMaster->density(); + std::vector& vBF = m_spInterfaceHandlerLocal->get_boundary_faces(); + +// initialize data (written during call of 'diffusive_flux_Jac()': + LocalIndices ind = u.get_indices(); + rotJ_ind.resize(ind); rotJ_ind = 0.0; + rotJ_rot.resize(ind); rotJ_rot = 0.0; + + for(size_t ip = 0; ip < vBF.size(); ++ip) + { + interfaceBF bf = vBF[ip]; + + if ( fluidDensity->value(0, ip) != fluidDensity->value(1, ip) ) + UG_THROW("ParticleBndCond::add_jac_M_elem(): density different for series 0 and 1: " + << fluidDensity->value(0, ip) << " != " << fluidDensity->value(1, ip) << "\n"); + + number importDensity = fluidDensity->value(0, ip); + + // The momentum equation: + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + diffusive_flux_Jac(ip, bf, J, u); + + // The continuity equation + if ( 1 ) //! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + { + for(size_t sh = 0; sh < bf.num_sh(); ++sh) // loop shape functions + for (size_t d2 = 0; d2 < (size_t)dim; ++d2) + J(_P_, bf.node_id(), d2, sh) += bf.shape(sh) * bf.normal()[d2] + * importDensity; + } + + } // end vBF + + // copy rotJ_ind/rotJ_rot to m_spInterfaceHandlerLocal data for access from mapper + if ( vBF.size() > 0 ) + copy_local_couplings_jac(); + + +} + + +//////////////////////////////////////////////////////////////////////////////// +// methods for adapting defect due to the bnd cond +//////////////////////////////////////////////////////////////////////////////// + +// removes the defects with IDs in 'vFctID' locally assembled in the corners of 'vDofID' +template +void ParticleBndCond:: +remove_equations(LocalVector& d, std::vector vFctID, std::vector vDofID) +{ + for(size_t i = 0; i < vDofID.size(); ++i) + for(size_t j = 0; j < vFctID.size(); ++j) + d(vFctID[j], vDofID[i]) = 0.0; + +} + +// see 'NavierStokesNoNormalStressOutflowFV1::diffusive_flux_defect()' +template +void ParticleBndCond:: +diffusive_flux_defect_rot(const size_t ip, const interfaceBF& bf, LocalVector& d, const LocalVector& u) +{ + int prtIndex = get_prtIndex(); + + MathVector transSol = m_spInterfaceHandlerLocal->get_transSol(prtIndex, 0); + MathVector rotSol = m_spInterfaceHandlerLocal->get_rotSol(prtIndex, 0); + const MathVector center = m_spInterfaceHandlerLocal->get_center(prtIndex); + + + MathMatrix gradVel; + MathVector diffFlux; + MathVector pressureRot; + + + MathVector RotDef; + MathMatrix rotationMatIP_transposed = m_spInterfaceHandlerLocal->get_rotationMat(m_spInterfaceHandlerLocal->radial_at_ip(bf.node_id())); + Transpose(rotationMatIP_transposed); + +// 1. Get the gradient of the velocity at ip + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + for(size_t d2 = 0; d2 < (size_t)dim; ++d2) + { + // sum up contributions of each shape + gradVel(d1, d2) = 0.0; + for(size_t sh = 0; sh < bf.num_sh(); ++sh) + { + if ( m_spInterfaceHandlerLocal->lies_onInterface(sh) ) + { + MathVector radialCo; + VecSubtract(radialCo, m_spInterfaceHandlerLocal->corner(sh), center); + MathMatrix rotationMatCo = m_spInterfaceHandlerLocal->get_rotationMat(radialCo); + // set solution + number sol = transSol[d1]; + for ( int d = 0; d < dim; ++d ) + sol += rotationMatCo[d1][d]*rotSol[d]; + + gradVel(d1, d2) += bf.global_grad(sh)[d2] * sol; + } + else + gradVel(d1, d2) += bf.global_grad(sh)[d2] * u(d1, sh); + } + } + + +// 2. Compute the total flux +// - add (\nabla u) \cdot \vec{n} + MatVecMult(diffFlux, gradVel, bf.normal()); + +// - add (\nabla u)^T \cdot \vec{n} + if( !m_spMaster->laplace()) + TransposedMatVecMultAdd(diffFlux, gradVel, bf.normal()); + +// 3. Scale by viscosity + diffFlux *= get_kinVisc_fluid(); + +// 4. compute r x [\sigma(u)] + MatVecMult(RotDef, rotationMatIP_transposed, diffFlux); + +// 5. Change sign, since force acts onto particle in inverse direction: + //diffFlux *= -1.0; + +// 6. Add flux to local defect + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + { + d(d1, bf.node_id()) += diffFlux[d1]; + rotD(d1, bf.node_id()) += RotDef[d1]; + } + +// 7. Add pressure term to local defect + number pressure = 0.0; + for(size_t sh = 0; sh < bf.num_sh(); ++sh) + pressure += bf.shape(sh) * u(_P_, sh); + + MatVecMult(pressureRot, rotationMatIP_transposed, bf.normal()); + if ( fabs(pressureRot[0]) > 1e-10 ) + UG_THROW("pressureRot " << pressureRot << "\n"); + + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + { + d(d1, bf.node_id()) -= pressure * bf.normal()[d1]; + rotD(d1, bf.node_id()) -= pressure * pressureRot[d1]; + + } +} + + +// see 'NavierStokesNoNormalStressOutflowFV1::diffusive_flux_defect()' +template +void ParticleBndCond:: +diffusive_flux_defect(const size_t ip, const interfaceBF& bf, LocalVector& d, const LocalVector& u) +{ + MathMatrix gradVel; + MathVector diffFlux; + +// 1. Get the gradient of the velocity at ip + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + for(size_t d2 = 0; d2 < (size_t)dim; ++d2) + { + // sum up contributions of each shape + gradVel(d1, d2) = 0.0; + for(size_t sh = 0; sh < bf.num_sh(); ++sh) + gradVel(d1, d2) += bf.global_grad(sh)[d2] * u(d1, sh); + } + +// 2. Compute the total flux + +// - add (\nabla u) \cdot \vec{n} + MatVecMult(diffFlux, gradVel, bf.normal()); + +// - add (\nabla u)^T \cdot \vec{n} + if( !m_spMaster->laplace()) + TransposedMatVecMultAdd(diffFlux, gradVel, bf.normal()); + +// 3. Scale by viscosity + diffFlux *= get_kinVisc_fluid(); + +// 4. Change sign, since force acts onto particle in inverse direction: + //diffFlux *= -1.0; + +// 5. Add flux to local defect + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + d(d1, bf.node_id()) += diffFlux[d1]; + + +// 6. Add pressure term to local defect + number pressure = 0.0; + for(size_t sh = 0; sh < bf.num_sh(); ++sh) + pressure += bf.shape(sh) * u(_P_, sh); + + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + d(d1, bf.node_id()) -= pressure * bf.normal()[d1]; + +} + +template +void ParticleBndCond:: +add_def_A_elem_Quadri_for2(LocalVector& locD, const LocalVector locU) +{ +// Loop the boundary faces to assemble impulse equations + SmartPtr > fluidDensity = m_spMaster->density(); + std::vector& vBF = m_spInterfaceHandlerLocal->get_boundary_faces(); + + if ( vBF.size() != 4 ) + UG_THROW("in 'ParticleBndCond::add_def_A_elem_Quadri_for2(): vBF.size() should be 4 but is " << vBF.size() << "\n"); + +// get density + number importDensity = fluidDensity->value(0, 0); + if ( fluidDensity->value(0, 0) != fluidDensity->value(1, 0) ) + UG_THROW("ParticleBndCond::add_def_A_elem_Quadri_for2(): density different for series 0 and 1: " + << fluidDensity->value(0, 0) << " != " << fluidDensity->value(1, 0) << "\n"); + if ( importDensity != get_density_fluid() ) + UG_THROW("ParticleBndCond::add_def_A_elem_Quadri_for2(): importDensity = " << importDensity << " = " + "fluidDensity = " << get_density_fluid() << "\n"); + +// loop all boundary faces + for(size_t ip = 0; ip < vBF.size(); ++ip) + { + UG_LOG("---- ip = " << ip << "\n"); + + interfaceBF bf = vBF[ip]; + + // Compute Velocity at ip + MathVector stdVel(0.0); + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + for(size_t sh = 0; sh < bf.num_sh(); ++sh) + stdVel[d1] += locU(d1, sh) * bf.shape(sh); + + // Momentum equation: + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + diffusive_flux_defect(ip, bf, locD, locU); + + // Continuity equation: + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + locD(_P_, bf.node_id()) += VecDot(stdVel, bf.normal()) * importDensity; + + } // end vBF-loop + +} + +template +void ParticleBndCond:: +write_QuadriSol(const LocalVector origU) +{ + +// initialize data + LocalIndices ind = origU.get_indices(); + LocalVector quadriU; + + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + { + for(size_t fct = 0; fct < ind.num_fct(); ++fct) + ind.resize_dof(fct, 5); + + if ( ind.num_dof(0) != 5 ) + UG_THROW("hmm: ind.num_dof(0) = " << ind.num_dof(0) << "\n"); + } + quadriU.resize(ind); + + // A. remap solution (velocity AND pressure!) of inside node + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + { + size_t copyID = m_spInterfaceHandlerLocal->m_vNOInterfaceID[0]; + for(size_t fct = 0; fct < dim+1; ++fct) + quadriU(fct,4) = origU(fct,copyID); + } + + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + { + for ( size_t i = 0; i < m_spInterfaceHandlerLocal->m_vNOInterfaceID.size(); ++i ) + UG_THROW("m_vNOInterfaceID[" << i << "]: " << m_spInterfaceHandlerLocal->m_vNOInterfaceID[i] << "\n"); + + } + + std::vector testV; + for(size_t dof = 0; dof < 4; ++dof) + testV.push_back(1.0+0.1*dof); + + // B. remap pressure solution in all interface nodes + for(size_t fct = 0; fct < dim+1; ++fct) + for(size_t dof = 0; dof < 4; ++dof) + { + quadriU(fct,dof) = testV[m_spInterfaceHandlerLocal->m_vQuadriOrigID[dof]]; //origU(fct,m_spInterfaceHandlerLocal->m_vQuadriOrigID[dof]); + } + + +// C. write velocities of the 2 particles: + MathVector transSol1 = m_spInterfaceHandlerLocal->get_transSol(0, 0); + MathVector rotSol1 = m_spInterfaceHandlerLocal->get_rotSol(0, 0); + MathVector transSol2 = m_spInterfaceHandlerLocal->get_transSol(1, 0); + MathVector rotSol2 = m_spInterfaceHandlerLocal->get_rotSol(1, 0); + +// resize local data as done during 'modify_LocalSol' +// --> but there with num_co = 3 for the Triangle with inside node! + for(size_t fct = 0; fct < ind.num_fct(); ++fct) + for(size_t dof = 0; dof < 4; ++dof) + { + MathMatrix rotationMatCo = m_spInterfaceHandlerLocal->get_rotationMat(m_spInterfaceHandlerLocal->radial_at_co(dof)); + + // write solution of particle with prtIndex = 0: + if ( dof < 2 ) + { + quadriU(fct,dof) = transSol1[fct]; + for ( int d = 0; d < dim; ++d ) + quadriU(fct,dof) += rotationMatCo[fct][d]*rotSol1[d]; + } + // write solution of particle with prtIndex = 1: + else + { + quadriU(fct,dof) = transSol2[fct]; + for ( int d = 0; d < dim; ++d ) + quadriU(fct,dof) += rotationMatCo[fct][d]*rotSol2[d]; + } + } +} + + + +template +void ParticleBndCond:: +add_quadri_to_defect(LocalVector& d, const LocalVector& quadriD) +{ + + for(size_t fct=0; fct < quadriD.num_all_fct(); ++fct) + { + for(size_t dof=0; dof < quadriD.num_all_dof(fct); ++dof) + { + if ( quadriD.value(fct,dof) != quadriD.value(fct,dof) ) + UG_THROW("NAN in 'add_quadri_to_defect()'!...\n"); + + bool isPrtNode = m_spInterfaceHandlerLocal->lies_onInterface(dof); + // usual assembling for fluid-dof and pressure-fct + if ( isPrtNode ) + { + size_t _dof = m_spInterfaceHandlerLocal->m_vQuadriOrigID[dof]; + + d.value(fct,_dof) += quadriD.value(fct,dof); + + } + + } + } + +} + + + +template +template +void ParticleBndCond:: +add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ +//////////////////////////////////////////////////////////////////////////////// +// (A) Remove local impuls equations for interface-corners +/////////////////////////////////////////////////////////////////////////////// + +// element modus was computed via 'compute_element_modus()' during 'update_interface_data()': + ElementModus elemModus = m_spInterfaceHandlerLocal->get_element_modus(elem); + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + { + if ( elemModus != INSIDE_DOM ) + { + // all impulse equations will be removed + std::vector vFctID(dim); + for(size_t i = 0; i < dim; ++i) vFctID[i] = i; + + // equations for interface nodes will be removed + std::vector vDoFID; vDoFID.clear(); + for(size_t i = 0; i < m_spInterfaceHandlerLocal->m_vInterfaceID.size(); ++i) + { + size_t interfaceID = m_spInterfaceHandlerLocal->m_vInterfaceID[i]; + size_t indexToRemove = interfaceID; + vDoFID.push_back(indexToRemove); + } + + remove_equations(d, vFctID, vDoFID); + + } + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// (B) If CUT_BY_2_INTERFACE or StdFVAssembling, no additional interface stresses will be computed +// => map existing entries +//////////////////////////////////////////////////////////////////////////////// + + if ( elemModus == CUT_BY_2_INTERFACE && !m_spInterfaceHandlerLocal->StdFV_assembling()) + { + write_QuadriSol(u); + + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + + // case == 4: all entries (also for pressure eq) will be written newly, since only boundary faces are relevant + // => during remove_equations() only velocity equations were removed! + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 4 ) + d = 0.0; + + // locU was written during 'ParticleMapper::modify_LocalData()': + if ( m_spInterfaceHandlerLocal->interface_id_all().size() == 4 || m_spInterfaceHandlerLocal->interface_id_all().size() == 2 ) + add_def_A_elem_Quadri_for2(d, u); + else + UG_THROW("in ParticleBndCond::add_def_A_elem(): wrong amount of interface.size() for CUT_BY_2_INTERFACE: " << m_spInterfaceHandlerLocal->interface_id_all().size() << " ( should be 2!)\n"); + + return; + } + +//////////////////////////////////////////////////////////////////////////////// +// (C) For CUT_BY_INTERFACE: compute stresses on inner boundary faces 'vBF' +// via 'diffusive_flux_def()' and add the to local defect +//////////////////////////////////////////////////////////////////////////////// + +// initialize data (written during call of 'diffusive_flux_Jac()': + LocalIndices ind = u.get_indices(); + rotD.resize(ind); rotD = 0.0; + +// Loop the boundary faces to assemble impulse equations + SmartPtr > fluidDensity = m_spMaster->density(); + std::vector& vBF = m_spInterfaceHandlerLocal->get_boundary_faces(); + + + if ( dim == 2 && vBF.size() > 2 ) + UG_THROW("add_def_A_elem(): vBF.size() is greater than 2: " << vBF.size() << "\n"); + + for(size_t ip = 0; ip < vBF.size(); ++ip) + { + interfaceBF bf = vBF[ip]; + + // Compute Velocity at ip + MathVector stdVel(0.0); + for(size_t d1 = 0; d1 < (size_t)dim; ++d1) + for(size_t sh = 0; sh < bf.num_sh(); ++sh) + stdVel[d1] += u(d1, sh) * bf.shape(sh); + + // Momentum equation: + if ( ! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + diffusive_flux_defect(ip, bf, d, u); + + // scale with deltaT ( = 1.0 for non-time-dependent) + // d *= deltaT; + // NOT necessary, since add_def_A_elem() will be multiplied by 'dt' + // during elem_dis_assemble_util.h ??? + + if ( fluidDensity->value(0, ip) != fluidDensity->value(1, ip) ) + UG_THROW("ParticleBndCond::add_jac_M_elem(): density different for series 0 and 1: " + << fluidDensity->value(0, ip) << " != " << fluidDensity->value(1, ip) << "\n"); + number importDensity = fluidDensity->value(0, ip); + if ( importDensity != get_density_fluid() ) + UG_THROW("ParticleBndCond::add_def_A_elem(): importDensity = " << importDensity << " != " + "fluidDensity = " << get_density_fluid() << "\n"); + + + // Continuity equation: + if ( 1 ) //! m_spInterfaceHandlerLocal->StdFV_assembling() ) // only remove, if NOT StdFVAssembling + d(_P_, bf.node_id()) += VecDot(stdVel, bf.normal()) * importDensity; + } + + copy_local_couplings_def(); + + +} +//////////////////////////////////////////////////////////////////////////////// +// methods for adapting mass matrix for bnd cond +//////////////////////////////////////////////////////////////////////////////// + +// instead of calling 'set_mass_and_inertia()' during 'add_local_mat_to_global_interface()' +template +template +void ParticleBndCond:: +add_jac_M_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + +// A. do nothing for inside elements + if ( modus == INSIDE_DOM ) + return; + +// B. for outside elements: geo.num_scv() = 0 +// => nothing added during NavierStokesFV1_cutElem::add_jac_M_elem +// => nothing will be subtracted here :) + + +/////////////////////////////////////////////////////////////////////////////// +// substract part added by NavierStokesFV1_cutElem::add_jac_M_elem(): +// (instead of setting scv.volume() to zero) +//////////////////////////////////////////////////////////////////////////////// + +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + static TFVGeom& geo = GeomProvider::get(LFEID(LFEID::LAGRANGE, dim, 1), 1); + + SmartPtr > fluidDensity = m_spMaster->density(); + +// loop Sub Control Volumes (SCV) + for(size_t ip = 0; ip < geo.num_scv(); ++ip) + { + // get SCV + const typename TFVGeom::SCV& scv = geo.scv(ip); + + // get associated node + const int sh = scv.node_id(); + + if ( !m_spInterfaceHandlerLocal->lies_onInterface(sh) ) + continue; + + if ( fluidDensity->value(0, ip) != fluidDensity->value(1, ip) ) + UG_THROW("ParticleBndCond::add_jac_M_elem(): density different for series 0 and 1: " + << fluidDensity->value(0, ip) << " != " << fluidDensity->value(1, ip) << "\n"); + + number importDensity = fluidDensity->value(0, ip); + + // loop velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + // Add to local matrix + J(d1, sh, d1, sh) -= scv.volume() * importDensity; + } + } + + return; + + + const int prtIndex = get_prtIndex(); + const number prtDensity = get_density(prtIndex); + +// if INSIDE_DOM: do nothing! + if ( modus != INSIDE_DOM ) + { + // get data + const ReferenceObjectID roid = elem->reference_object_id(); + const DimReferenceElement& rRefElem + = ReferenceElementProvider::get(roid); + + // OUTSIDE_DOM + if ( modus == OUTSIDE_DOM ) + { + // loop corners of reference element + for(size_t sh = 0; sh < rRefElem.num(0); ++sh) + { + // loop velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + // Add to local matrix + J(d1, sh, d1, sh) += geo.volume_fem_elem() * prtDensity; + // rescale volume fraction + J(d1, sh, d1, sh) *= 1.0/rRefElem.num(0); + } + } + } + // CUT_BY_INTERFACE + else if ( modus == CUT_BY_INTERFACE ) + { + // loop corners of reference element + for(size_t sh = 0; sh < rRefElem.num(0); ++sh) + { + // loop velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + // Add to local matrix + J(d1, sh, d1, sh) += geo.volume_fem_elem() * prtDensity; + UG_THROW("geo.volume_fem_elem() = " << geo.volume_fem_elem() << "\n"); + // rescale volume fraction + J(d1, sh, d1, sh) *= 1.0/rRefElem.num(0); + } + } + } + else + UG_THROW("ParticleBndCond::add_jac_M_elem()..."); + + } // end 'if ( !is_inside_elem() )' + + +} + + +template +template +void ParticleBndCond:: +add_def_M_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + +// A. do nothing for inside elements + if ( modus == INSIDE_DOM ) + return; + +// B. for outside elements: geo.num_scv() = 0 +// => nothing added during NavierStokesFV1_cutElem::add_jac_M_elem +// => nothing will be subtracted here :) + + +/////////////////////////////////////////////////////////////////////////////// +// substract part added by NavierStokesFV1_cutElem::add_jac_M_elem(): +// (instead of setting scv.volume() to zero) +//////////////////////////////////////////////////////////////////////////////// + +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + static TFVGeom& geo = GeomProvider::get(LFEID(LFEID::LAGRANGE, dim, 1), 1); + + SmartPtr > fluidDensity = m_spMaster->density(); + +// loop Sub Control Volumes (SCV) + for(size_t ip = 0; ip < geo.num_scv(); ++ip) + { + // get current SCV + const typename TFVGeom::SCV& scv = geo.scv(ip); + + // get associated node + const int sh = scv.node_id(); + + if ( !m_spInterfaceHandlerLocal->lies_onInterface(sh) ) + continue; + + if ( fluidDensity->value(0, ip) != fluidDensity->value(1, ip) ) + UG_THROW("ParticleBndCond::add_jac_M_elem(): density different for series 0 and 1: " + << fluidDensity->value(0, ip) << " != " << fluidDensity->value(1, ip) << "\n"); + + number importDensity = fluidDensity->value(0, ip); + + // loop velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + // Add to local matrix + d(d1, sh) -= u(d1, sh) * scv.volume() * importDensity; + } + } + + +} + +//////////////////////////////////////////////////////////////////////////////// +/// methods for adapting the rhs due to the bnd cond +//////////////////////////////////////////////////////////////////////////////// + +// here gravity force -> independent of velocity solution! +template +template +void ParticleBndCond:: +add_rhs_elem(LocalVector& d, GridObject* elem, const MathVector vCornerCoords[]) +{ + // no additional rhs, already done by the local-to-global mapper + return; + + + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + +// A. do nothing for inside elements + if ( modus == INSIDE_DOM ) + return; + +// B. for outside elements: geo.num_scv() = 0 +// => nothing added during NavierStokesFV1_cutElem::add_jac_M_elem +// => nothing will be subtracted here :) + + +/////////////////////////////////////////////////////////////////////////////// +// substract part added by NavierStokesFV1_cutElem::add_rgh_elem(): +// (instead of setting scv.volume() to zero) +//////////////////////////////////////////////////////////////////////////////// + +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + static TFVGeom& geo = GeomProvider::get(LFEID(LFEID::LAGRANGE, dim, 1), 1); + +// loop Sub Control Volumes (SCV) + for(size_t ip = 0; ip < geo.num_scv(); ++ip) + { + // get current SCV + const typename TFVGeom::SCV& scv = geo.scv(ip); + + // get associated node + const int sh = scv.node_id(); + + if ( !m_spInterfaceHandlerLocal->lies_onInterface(sh) ) + continue; + + // Add to local matrix + d(0, sh) -= scv.volume() * (-9.81); + + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +// register assemble functions +//////////////////////////////////////////////////////////////////////////////// + +#ifdef UG_DIM_1 +template<> +void ParticleBndCond:: +register_all(bool bHang) +{ +// switch assemble functions + if(!bHang) + { + register_func > >(); + +// register_func >(); + } + else + { + UG_THROW("ParticleBndCond: Hanging Nodes not implemented.") + } +} +#endif + +#ifdef UG_DIM_2 +template<> +void ParticleBndCond:: +register_all(bool bHang) +{ +// switch assemble functions + if(!bHang) + { + register_func > >(); +/* + register_func >(); + register_func >(); + */ + } + else + { + UG_THROW("ParticleBndCond: Hanging Nodes not implemented.") + } +} +#endif + +#ifdef UG_DIM_3 +template<> +void ParticleBndCond:: +register_all(bool bHang) +{ +// switch assemble functions + if(!bHang) + { + register_func > >(); +/* + register_func >(); + register_func >(); + register_func >(); + register_func >(); + */ + } + else + { + UG_THROW("ParticleBndCond: Hanging Nodes not implemented.") + } +} +#endif + +template +template +void +ParticleBndCond:: +register_func() +{ + ReferenceObjectID id = geometry_traits::REFERENCE_OBJECT_ID; + typedef ParticleBndCond T; + + this->clear_add_fct(id); + this->set_prep_elem_loop_fct( id, &T::template prep_elem_loop); + this->set_prep_elem_fct( id, &T::template prep_elem); + this->set_fsh_elem_loop_fct( id, &T::template fsh_elem_loop); + this->set_add_jac_A_elem_fct( id, &T::template add_jac_A_elem); + this->set_add_jac_M_elem_fct( id, &T::template add_jac_M_elem); + this->set_add_def_A_elem_fct( id, &T::template add_def_A_elem); + this->set_add_def_M_elem_fct( id, &T::template add_def_M_elem); + this->set_add_rhs_elem_fct( id, &T::template add_rhs_elem); +} + + +//////////////////////////////////////////////////////////////////////////////// +// explicit template instantiations +//////////////////////////////////////////////////////////////////////////////// + +#ifdef UG_DIM_1 +template class ParticleBndCond; +#endif +#ifdef UG_DIM_2 +template class ParticleBndCond; +#endif +#ifdef UG_DIM_3 +template class ParticleBndCond; +#endif + + +} // end namespace NavierStokes +} // end namespace ug + + +#endif /* PARTICLE_BND_COND_IMPL_H_ */ diff --git a/incompressible/fv1/moving_particle/loc_to_glob_mapper_particle.h b/incompressible/fv1/moving_particle/loc_to_glob_mapper_particle.h new file mode 100644 index 0000000..f06e030 --- /dev/null +++ b/incompressible/fv1/moving_particle/loc_to_glob_mapper_particle.h @@ -0,0 +1,424 @@ +/* + * moving_particle.h + * + * Created on: 20.01.2015 + * Author: suze + */ + +#ifndef PARTICLE_MAPPER_H_ +#define PARTICLE_MAPPER_H_ + + +#ifdef UG_PARALLEL + #include "lib_grid/parallelization/load_balancer_util.h" +#endif + +#include "lib_disc/spatial_disc/immersed_util/immersed_interface_base.h" + +namespace ug{ +namespace NavierStokes{ + + + +template +class ParticleMapper : public IInterfaceMapper +{ + public: + /// World dimension + static const int dim = TDomain::dim; + + /// abbreviation for pressure + static const size_t _P_ = dim; + + /// Algebra type + typedef TAlgebra algebra_type; + + /// Type of algebra matrix + typedef typename algebra_type::matrix_type matrix_type; + + /// Type of algebra vector + typedef typename algebra_type::vector_type vector_type; + + /// Type of geometric base object + typedef typename domain_traits::grid_base_object grid_base_object; + + /// used boundary face type + typedef typename DimFV1FTGeometry >::BF interfaceBF; + + public: + /// call base class constructor + ParticleMapper(SmartPtr > localHandler) + : m_spInterfaceHandlerLocal(localHandler), + m_spCutElementHandler(m_spInterfaceHandlerLocal->get_cutElementHandler()), + m_gravityConst(0.0), + m_bGravity(true), + m_bRepulsiveForce(false), + m_bGlowRepulsiveForce(false), + m_bMinimumCorrectionForce(false), + m_bForceLog(false), + m_rho(0.0), + m_epsilon(0.0), + m_repulsiveForce(0.0), + m_repulsiveDistance(0.0), + m_dt(0.0), + m_bTimeDep(false), + m_meanDiameter(0.0), + m_volume(0.0), + m_bVolumeCompExact(true), + m_bUsualAss(false) + { + // init boolian arrays for all registered particles + size_t numPrt = num_particles(); + m_bFlagGravity.resize(numPrt, false); + m_bFlagInertial.resize(numPrt, false); + }; + + /// destructor + virtual ~ParticleMapper() {}; + + /////////////////////////////////////////////////////////////////////////////// + /// + /// base class methods not needed for this class + /// + /////////////////////////////////////////////////////////////////////////////// + + void adjust_mat_global(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd){}; + + /////////////////////////////////////////////////////////////////////////////// + /// + /// A. send local entries to global vector + /// + /////////////////////////////////////////////////////////////////////////////// + + /// base method + virtual void add_local_vec_to_global(vector_type& vec, const LocalVector& lvec, ConstSmartPtr dd); + + /// methods called by base method for cut element case + virtual void add_local_vec_to_global_interface(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd); + + /// methods called by base method for the case, that TWO elements are cut by interface + /// --> tag: CUT_BY_2_INTERFACE + /// REMARK: not finally tested!! + virtual void add_local_vec_to_global_interface_for2(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd); + + /// central method to write/map the entries for fluid-particle coupling + virtual void add_local_vec_to_global_FT(vector_type& vec, const LocalVector& lvec, std::vector transInd, + std::vector rotInd); + + /// sends local entries to particle DoFs in case of two particles, cutting an element + /// --> tag: CUT_BY_2_INTERFACE + /// REMARK: not finally tested!! + virtual void add_local_vec_to_global_FT_for2_StdFV(vector_type& vec, const LocalVector& lvec, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2); + virtual void add_local_vec_to_global_FT_for2(vector_type& vec, const LocalVector& lvec, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2); + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// B. assemble components of the rhs for the particle + /// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // for time dependent case + void add_mass_part_def(vector_type& vec, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex); + + // called during 'add_local_vec_to_global_interface()': calls according components for assemgling the rhs + // (see methods below) + void add_rhs(vector_type& vec, std::vector transInd, std::vector rotInd, + const int levIndex, const int prtIndex); + + // additional methods called by 'add_rhs()': + void set_gravitational_rhs(vector_type& vec, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex); + void add_repulsive_force_rhs(vector_type& vec, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex); + void add_glowinski_repulsive_force_rhs(vector_type& vec, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex); + void add_minimum_correction_force_rhs(vector_type& vec, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex); + + + /////////////////////////////////////////////////////////////////////////////// + /// helper function for 'add_local_vec_to_global_FT_for2()': + /// --> tag: CUT_BY_2_INTERFACE + // REMARK: not finally tested! + + /// special mapping of local DoFs due to element, cut by two particles + size_t map_for2(size_t dof); + + /// called during 'add_local_vec_to_global_FT_for2()': + void assemble_QuadriCorners(vector_type& vec, const LocalVector& lvec, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2); + + + /////////////////////////////////////////////////////////////////////////////// + /// + /// C. local matrix to global + /// + /////////////////////////////////////////////////////////////////////////////// + + + /// base method + virtual void add_local_mat_to_global(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd); + + /// methods called by base method for cut element case + virtual void add_local_mat_to_global_interface(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd); + + /// methods called by base method for the case, that TWO elements are cut by interface + /// --> tag: CUT_BY_2_INTERFACE + /// REMARK: not finally tested!! + virtual void add_local_mat_to_global_interface_for2(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd); + + /// central method to write/map the entries for fluid-particle coupling --> tag: CUT_BY_INTERFACE + virtual void add_local_mat_to_global_FT(matrix_type& mat, const LocalMatrix& lmat, + std::vector transInd, std::vector rotInd); + + /// sends local entries to particle DoFs in case of two particles, cutting an element + /// --> tag: CUT_BY_2_INTERFACE + /// REMARK: not finally tested!! + virtual void add_local_mat_to_global_FT_for2_StdFV(matrix_type& mat, const LocalMatrix& lmat, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2); + virtual void add_local_mat_to_global_FT_for2(matrix_type& mat, const LocalMatrix& lmat, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2); + + + // for time dependent case + void add_mass_part_jac(matrix_type& mat, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex); + + /// sets dirichlet rows for DoFs lying outside the fluid + /// -> IFF element does NOT include transInd/rotInd-node! (which also lie outside fluid, + /// i.e. inside the particle domain + void set_identity_mat_constraint(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd); + + /////////////////////////////////////////////////////////////////////////////// + /// REMARK: + /// During DomainDiscretization::assemble_jacobian: + /// calling + /// ---> m_spAssTuner->modify_LocalData(pModifyMemory, vSol, dd); + /////////////////////////////////////////////////////////////////////////////// + + /// resizes local solution vector for the assemlby on cut elements, which + /// potentially have more nodes than the original element + virtual void modify_LocalData(LocalMatrix& locJ, LocalVector& locU, + ConstSmartPtr dd); + virtual void modify_LocalData(LocalVectorTimeSeries& uT, LocalMatrix& locJ, + LocalVector& locU, ConstSmartPtr dd); + + /// (1) resizes local solution vector for the assemlby on cut elements, which + /// potentially have more nodes than the original element + /// (2) writes the particle velocities in the designated, local particle indices + virtual void modify_LocalData(LocalVector& locD, LocalVector& tmpLocD, LocalVector& locU, + ConstSmartPtr dd); + virtual void modify_LocalData(LocalVectorTimeSeries& uT, LocalVector& locD, LocalVector& tmpLocD, + LocalVector& locU, ConstSmartPtr dd, size_t t); + + /// method called during 'modify_LocalData()' to set local sol for CUT_BY_2_INTERFACE + void set_QuadriSol(LocalVector& locU, LocalVector& locD); + + /// called during 'modify_LocalData(locU)': + /// copies old local vector to the new local algebra due to cut element + void map_local_data(LocalVector& d); + + /// called during modify_LocalData() for resizing local data on cut elements: + void resize_local_indices(LocalVector& locU) + { m_spInterfaceHandlerLocal->resize_local_indices(locU); } + void resize_local_indices(LocalVector& locU, size_t numCo) + { m_spInterfaceHandlerLocal->resize_local_indices(locU, numCo); } + + // writes/copies the linear and angular velocity of the particle to data storage + // in 'ParticleProvider:m_vvLinearVelocity' (called during 'modify_GlobalSol') + void set_extraSol(vector_type& vec, std::vector transInd, std::vector rotInd, + const int timeIndex, const int prtIndex); + + /////////////////////////////////////////////////////////////////////////////// + /// REMARK: + /// During DomainDiscretization::assemble_jacobian: + /// calling + /// ---> m_spAssTuner->modify_GlobalSol(pModifyMemory, vSol, dd); + /////////////////////////////////////////////////////////////////////////////// + + /// the method 'modify_GlobalSol()' does not 'modify' the solution, but: the + /// computed solution at the trans und rot DoFs gets written/stored into data of + /// class 'ParticleProvider::m_vvLinearVelocity/m_vvAngularVelocity', + /// since they will be overwritten during the local computation of defect + /// (for each call of domainDisc, i.e. newton step) + + virtual void modify_GlobalSol(vector_type& uMod, const vector_type& u, ConstSmartPtr dd); + + virtual void modify_GlobalSol(SmartPtr > vSolMod, + ConstSmartPtr > vSol, ConstSmartPtr dd); + + /////////////////////////////////////////////////////////////////////////////// + /// setter methods: + /////////////////////////////////////////////////////////////////////////////// + + void set_gravity(bool gravity, number gravityConst) + { m_bGravity = gravity; m_gravityConst = gravityConst;} + void set_repulsive_force(bool repulsive, number forceValue) + {m_bRepulsiveForce = repulsive; m_repulsiveForce = forceValue;} + void set_glowinski_repulsive_force(bool repulsive, number rho, number epsilon) + {m_bGlowRepulsiveForce = repulsive; m_rho = rho; m_epsilon = epsilon;} + void set_minimum_correction_force(bool repulsive, std::vector > equiDist) + {m_bMinimumCorrectionForce = repulsive; m_repulsiveDistance = equiDist;} + + void set_bUsualAss(bool UsualAss) { m_bUsualAss = UsualAss; } + void set_volume_comp_mode(bool bVolumeCompMode) { m_bVolumeCompExact = bVolumeCompMode;} + void set_time_step(number dt) { m_dt = dt; set_time_dependent(true);} + void set_time_dependent(bool bTimeDep) { m_bTimeDep = bTimeDep; } + void set_element_diameter(double diameter) { m_meanDiameter = diameter;} + + // flag for output + void set_forceLog(bool val) {m_bForceLog = val;} + + + /////////////////////////////////////////////////////////////////////////////// + /// getter methods: + /////////////////////////////////////////////////////////////////////////////// + + // gets the current index of the particle, which cuts the current element; + // stored in 'InterfaceHandlerLocalParticle' + int get_prtIndex() { return this->m_spInterfaceHandlerLocal->get_prtIndex(); } + + // IFF two particles cut an element: gets the index of the particle, to which + // the node with local index 'dof' belongs + int getPrtIndex(size_t dof) { return m_spInterfaceHandlerLocal->getPrtIndex(dof);} + + size_t num_particles() const { return m_spCutElementHandler->num_particles();} + bool UsualAss() { return m_bUsualAss; } + bool is_time_dependent() { return m_bTimeDep;} + bool gravitation_force() { return m_bGravity; } + + number get_time_step() + { if ( !is_time_dependent() ) + UG_THROW("Call for time step, BUT: not timedependent computation!\n"); + return m_dt; } + + /// checks if grid data is updated and returns the 'levIndex' of the 'gridLevel' + /// via access to 'CutElementHandlerBase::m_Map' + int get_Index(const GridLevel& gridLevel, ConstSmartPtr dd) + { return m_spCutElementHandler->get_Index(gridLevel, dd); } + + ///////////////////////////////////////////////////////////////////////////////////// + // the mapping modus inherits the coupling categorie + // --> pair combinations (from,to) of: velDoF in fluid, velDoF in particle, + // pressureDoF in fluid, pressureDof on particle boundary + // + // => for the case 'pressureDof on particle boundary': is treated as usual DoF + // => ONLY for the case 'velDoF in particle' mapping to the global index needs to be + // changed (for the row or column) and in case of rotational DoF also different + // values (multiplied by rotationMat) need to be choosen + ///////////////////////////////////////////////////////////////////////////////////// + + int getMappingModus(size_t fct1, size_t dof1, size_t fct2, size_t dof2); + int getMappingModus_for2(size_t fct1, size_t dof1, size_t fct2, size_t dof2); + + + // access to 'LocalMatrix' data within 'ParticleBndCond' class + // (used during 'add_local_mat_to_global_FT()' in case the boundary + // conditions were computed by the class 'ParticleBndCond') + number get_rotJ_ind(size_t fct1, size_t dof1, size_t fct2, size_t dof2) + { return m_spInterfaceHandlerLocal->get_rotJ_ind(fct1, dof1, fct2, dof2); } + number get_rotJ_rot(size_t fct1, size_t dof1, size_t fct2, size_t dof2) + { return m_spInterfaceHandlerLocal->get_rotJ_rot(fct1, dof1, fct2, dof2); } + + number get_rotD(size_t fct, size_t dof) + { return m_spInterfaceHandlerLocal->get_rotD(fct, dof); } + + + /////////////////////////////////////////////////////////////////////////////// + /// Forwarding computational methods to the interface provider: + /////////////////////////////////////////////////////////////////////////////// + + // analytical computation of the volume of a dics/sphere + number Volume(int levIndex, size_t prtIndex) + { return m_spCutElementHandler->Volume(levIndex, prtIndex); } + number Mass(const int levIndex, const int prtIndex) + { return m_spCutElementHandler->Mass(levIndex, prtIndex, m_spInterfaceHandlerLocal->get_density_fluid()); } + number Mass(const int levIndex, const int prtIndex, const number volume) + { return m_spCutElementHandler->Mass(levIndex, prtIndex, volume, m_spInterfaceHandlerLocal->get_density_fluid()); } + number MomOfInertia(const int levIndex, const int prtIndex) + { return m_spCutElementHandler->MomOfInertia(levIndex, prtIndex, m_spInterfaceHandlerLocal->get_density_fluid()); } + number MomOfInertia(const int levIndex, const int prtIndex, const number volume) + { return m_spCutElementHandler->MomOfInertia(levIndex, prtIndex, volume, m_spInterfaceHandlerLocal->get_density_fluid()); } + + // if m_bVolumeCompExact = false: the volume will computed based on the volume of the parts + // covered by in the particle, instead of using the analytical formular for a disc/sphere + number compute_volume(int levIndex, size_t prtIndex); + void reset_volume(){m_volume = 0.0;} + + /////////////////////////////////////////////////////////////////////////////// + /// class member + /////////////////////////////////////////////////////////////////////////////// + + private: + // member from base class + SmartPtr > m_spInterfaceHandlerLocal; + // new member + SmartPtr > m_spCutElementHandler; + + + // gravityConst for call during 'set_gravitational_rhs()' + number m_gravityConst; + + // boolian to add gravity force to global defect during 'add_local_vec_to_global_interface()' + bool m_bGravity; // default = false; + + /// handles the call of'set_gravity()' exactly ONCE during 'modify_LocalData()' + std::vector m_bFlagGravity; // default = false + + /// handles the call of'set_gravity()' exactly ONCE during 'modify_LocalData()' + std::vector m_bFlagInertial; // default = false + + + // boolian to add repulsive force in 'add_rhs()' + bool m_bRepulsiveForce; + bool m_bGlowRepulsiveForce; + bool m_bMinimumCorrectionForce; + bool m_bForceLog; + number m_rho; + number m_epsilon; + number m_repulsiveForce; + std::vector > m_repulsiveDistance; + + // used within 'set_gravitational_rhs()' for computation of rhs + number m_dt; // default = 0.0; + bool m_bTimeDep; + + // used within 'add_repulsive_force' for computation of rhs + double m_meanDiameter; + + // used for the iterative computation of the volume, if not computet analytically + number m_volume; + bool m_bVolumeCompExact; // default = true; if false, the volume will computed based + // on the volume of the parts covered by in the particle, + // instead of using the analytical formular for a disc/sphere + + bool m_bUsualAss; // default = false; + + +}; + + +} // end namespace NavierStokes +} // end namespace ug + + +#include "loc_to_glob_mapper_particle_impl.h" + + + +#endif /* PARTICLE_MAPPER_H_ */ diff --git a/incompressible/fv1/moving_particle/loc_to_glob_mapper_particle_impl.h b/incompressible/fv1/moving_particle/loc_to_glob_mapper_particle_impl.h new file mode 100644 index 0000000..a0ea82d --- /dev/null +++ b/incompressible/fv1/moving_particle/loc_to_glob_mapper_particle_impl.h @@ -0,0 +1,2187 @@ +/* + * moving_particle_impl.h + * + * Created on: 20.01.2015 + * Author: suze + */ + +#ifndef PARTICLE_MAPPER_IMPL_H_ +#define PARTICLE_MAPPER_IMPL_H_ + +namespace ug { +namespace NavierStokes { + +/////////////////////////////////////////////////////////// +// Implementation of the methods class +// 'ParticleHandlerGlobal' +/////////////////////////////////////////////////////////// + +// see particle_handler_global_impl.h/_tools.h + + +/////////////////////////////////////////////////////////// +// Implementation of the methods class +// 'ParticleMapper' +/////////////////////////////////////////////////////////// + + +template +number ParticleMapper:: +compute_volume(int levIndex, size_t prtIndex) +{ + bool output = false; + + if (dim == 2 && m_spInterfaceHandlerLocal->m_vBF.size() != 2 + && m_spInterfaceHandlerLocal->m_vBF.size() != 0) { + UG_LOG("m_spInterfaceHandlerLocal->m_vBF.size() = " << m_spInterfaceHandlerLocal->m_vBF.size() << "\n"); + UG_THROW("oha, this->m_vBF.size() != 2 && != 0:\n"); + } + + const bool useResized = true; + number dist = 0.0; + number baseArea = 0.0; + number height = 0.0; + number volume = 0.0; + MathVector midPoint(0.0); + std::vector > vCorners; + + const MathVector center = m_spCutElementHandler->get_center( + prtIndex); + std::vector < interfaceBF > &vBF = + m_spInterfaceHandlerLocal->get_boundary_faces(); + +// loop bf + for (size_t i = 0; i < vBF.size(); ++i) { + interfaceBF bf = vBF[i]; + + size_t nodeID = bf.node_id(); + if (!useResized) + nodeID = m_spInterfaceHandlerLocal->corner_orig(nodeID); + + vCorners.push_back(m_spInterfaceHandlerLocal->corner(nodeID)); + midPoint += m_spInterfaceHandlerLocal->corner(nodeID); + + baseArea += VecLength(bf.normal()); + + if (output) { + UG_LOG("i = " << i << "\n"); + UG_LOG("nodeID = " << nodeID << "\n"); + UG_LOG( + "this->corner(" << nodeID << ") = " << m_spInterfaceHandlerLocal->corner(nodeID) << "\n"); + UG_LOG("vCorners[" << i << "] = " << vCorners[i] << "\n"); + UG_LOG("midPoint = " << midPoint << "\n"); + UG_LOG( + "VecLength(bf.normal()) = " << VecLength(bf.normal()) << "\n"); + UG_LOG("baseArea = " << baseArea << "\n"); + } + } + + if (dim == 2 && vCorners.size() != 2 && vCorners.size() != 0) + UG_THROW("vCorners.size() != 2:" << vCorners.size() << "\n"); + + midPoint *= 0.5; + height = VecDistance(midPoint, center); + dist = VecDistance(vCorners[0], vCorners[1]); + + if (fabs(baseArea - dist) > 1e-9) + UG_THROW( + "baseArea != dist(corners): " << baseArea << " != " << dist << "\n"); + + volume = 0.5 * baseArea * height; + + if (output) { + UG_LOG("midPoint = " << midPoint << "\n"); + UG_LOG("height = " << height << "\n"); + UG_LOG("dist = " << dist << "\n"); + UG_LOG("return: volume = " << volume << "\n"); + } + +// update volume: + m_volume += volume; + + if (output) { + UG_LOG("nachher (levIndex = " << levIndex << "): m_volume = " << m_volume << "\n"); + UG_LOG("ref Volume = " << Volume(levIndex,prtIndex) << "\n"); + } + + return volume; + +} + +template +int ParticleMapper:: +getMappingModus(size_t fct1, size_t dof1, size_t fct2, size_t dof2) +{ + +// if 'dof1' lies on interface, it is a particle from-DoF, i.e. momEq for particle +// if 'dof2' lies on interface, it is a particle to-DoF + bool isPrtNode = m_spInterfaceHandlerLocal->remapped_fromInterface(dof1); + bool isPrtConn = m_spInterfaceHandlerLocal->remapped_fromInterface(dof2); + +// fluidDoF -> fluidDoF: + if (!isPrtNode && !isPrtConn) + return 0; +// fluidDoF -> prtDoF: + if (!isPrtNode && isPrtConn) { + // fluidDoF -> interfacePressure: + if (fct2 == dim) + return 0; // = connection to pressure on interface! + else + return 1; + } +// prtDoF -> fluidDoF: + if (isPrtNode && !isPrtConn) { + // prtPressure -> fluidDoF + if (fct1 == dim) + return 0; // = ContEq on interface! + else + return 2; + } +// prtDoF -> prtDoF: + if (isPrtNode && isPrtConn) { + // interfacePressure -> interfacePressure + if (fct1 == dim && fct2 == dim) + return 0; + else if (fct1 == dim) + return 1; // prtDoF in ContEq => = fluidDoF -> prtDoF + else if (fct2 == dim) { + if (m_spInterfaceHandlerLocal->elementModus() != OUTSIDE_DOM) + return 2; // pressure in prt-ImpEq => prtDoF -> fluidDoF + else + return 4; + } else + return 3; // trans-rot/rot-trans + } + + return -1; + +} + +template +int ParticleMapper:: +getMappingModus_for2(size_t fct1, size_t dof1, size_t fct2, size_t dof2) +{ +// fluid->... + if (fct1 == _P_ || dof1 == 4) { + // fluid->fluid: + if (fct2 == _P_ || dof2 == 4) + return 0; + // fluid->particle: + else + return 1; + } +// particle->... + else { + // particle->fluid: + if (fct2 == _P_ || dof2 == 4) + return 2; + // particle->particle: + else + return 3; + } + + return -1; + +} + +template +void ParticleMapper:: +set_extraSol(vector_type& vec, std::vector transInd, std::vector rotInd, + const int timeIndex, const int prtIndex) +{ + for (int d = 0; d < dim; ++d) + { + // A. write linear velocity to data storage in 'ParticleProvider:m_vvLinearVelocity' + // --> if the linear velocity is given by the user, it does NOT need to be set + if (!m_spCutElementHandler->get_DoF_modus_linear(prtIndex)) + m_spCutElementHandler->set_extraSolTrans( + DoFRef(vec, transInd[d]), prtIndex, timeIndex, d); + + // B. write angular velocity to data storage in 'ParticleProvider:m_vvAngularVelocity' + // --> if the angular velocity is given by the user, it does NOT need to be set + if (!m_spCutElementHandler->get_DoF_modus_anguar(prtIndex)) + m_spCutElementHandler->set_extraSolRot( + DoFRef(vec, rotInd[d]), prtIndex, timeIndex, d); + } +} + +template +void ParticleMapper:: +modify_GlobalSol( + SmartPtr > vSolMod, + ConstSmartPtr > vSol, + ConstSmartPtr dd) +{ + bool output = false; + +// some checks + if (vSol->size() != 2) + UG_THROW("ParticleMapper::modify_GlobalSol: method needs exactly two time points."); + if (!is_time_dependent()) + set_time_dependent(true); + + const int levIndex = get_Index(dd->grid_level(), dd); + + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) { + m_bFlagInertial[p] = true; + + if (gravitation_force()) { + m_bFlagGravity[p] = true; + } + + // iFF linear AND angular velocity are given by the user: no storing necessary + if (m_spCutElementHandler->get_DoF_modus_linear(p) + && m_spCutElementHandler->get_DoF_modus_angular(p)) + continue; + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order to + // indicate, whether a particle lies on a processor or not + std::vector ElemList = + m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 NavierStokes::modify_GlobalSol: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 NavierStokes::modify_GlobalSol: ElemList.size(): " + << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + + // REMARK: 'get_transInd()' returns NOT YET updated indices (updated during 'movingParticle:update()' vie lua) + std::vector < DoFIndex > transInd = m_spCutElementHandler->get_transInd(levIndex, p); + std::vector < DoFIndex > rotInd = m_spCutElementHandler->get_rotInd(levIndex, p); + + if ( output ){ + UG_LOG("in modify_GlobalSol: transInd: " << transInd[0] << "\n"); + UG_LOG("in modify_GlobalSol: rotInd: " << rotInd[0] << "\n"); + + UG_LOG("VORHER: modify_GlobalSol transSol(0) = " << m_spCutElementHandler->get_transSol(p,0) << "\n"); + UG_LOG("VORHER: modify_GlobalSol transSol(1) = " << m_spCutElementHandler->get_transSol(p, 1) << "\n"); + UG_LOG("VORHER: modify_GlobalSol rotSol(0) = " << m_spCutElementHandler->get_rotSol(p,0) << "\n"); + UG_LOG("VORHER: modify_GlobalSol rot1Sol(1) = " << m_spCutElementHandler->get_rotSol(p, 1) << "\n"); + } + + // loop all time points and assemble them + for (int i = vSol->size() - 1; i >= 0; --i) { + number solution; + + for (int d = 0; d < dim; ++d) + { + // A. if the linear velocity is given by the user, it needs NOT to be set + if (!m_spCutElementHandler->get_DoF_modus_linear(p)) + { + solution = DoFRef(*vSol->solution(i), transInd[d]); + m_spCutElementHandler->set_extraSolTrans(solution, p, i, d); + } + + // B. if the angular velocity is given by the user, is needs NOT to be set + if (!m_spCutElementHandler->get_DoF_modus_angular(p)) + { + solution = DoFRef(*vSol->solution(i), rotInd[d]); + m_spCutElementHandler->set_extraSolRot(solution, p, i, d); + } + } + + } // end time series loop + + if ( output ){ + UG_LOG("NACHHER: modify_GlobalSol transSol(0) = " << m_spCutElementHandler->get_transSol(p,0) << "\n"); + UG_LOG("NACHHER: modify_GlobalSol transSol(1) = " << m_spCutElementHandler->get_transSol(p, 1) << "\n"); + UG_LOG("NACHHER: modify_GlobalSol rotSol(0) = " << m_spCutElementHandler->get_rotSol(p,0) << "\n"); + UG_LOG("NACHHER: modify_GlobalSol rot1Sol(1) = " << m_spCutElementHandler->get_rotSol(p, 1) << "\n"); + } + } // end particle loop + +} + +template +void ParticleMapper:: +modify_GlobalSol(vector_type& uMod, + const vector_type& u, ConstSmartPtr dd) +{ + bool output = false; + + if (is_time_dependent()) + set_time_dependent(false); + + const int levIndex = get_Index(dd->grid_level(), dd); + + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) + { + if (gravitation_force()) { + m_bFlagGravity[p] = true; + } + + // IFF linear AND angular velocity are given by the user: no need for storing + if (m_spCutElementHandler->get_DoF_modus_linear(p) + && m_spCutElementHandler->get_DoF_modus_angular(p)) { + continue; + } + + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order to + // indicate, whether a particle lies on a processor or not + std::vector ElemList = + m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 NavierStokes::modify_GlobalSol: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 NavierStokes::modify_GlobalSol: ElemList.size(): " + << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + + // REMARK: 'get_transInd()' returns NOT YET updated indices (updated during 'movingParticle:update()' vie lua) + std::vector < DoFIndex > transInd = m_spCutElementHandler->get_transInd(levIndex, p); + std::vector < DoFIndex > rotInd = m_spCutElementHandler->get_rotInd(levIndex, p); + + if (output ){ + UG_LOG("VORHER: modify_GlobalSol transSol(0) = " << m_spCutElementHandler->get_transSol(p,0) << "\n"); + UG_LOG("VORHER: modify_GlobalSol transSol(1) = " << m_spCutElementHandler->get_transSol(p, 1) << "\n"); + UG_LOG("VORHER: modify_GlobalSol rotSol(0) = " << m_spCutElementHandler->get_rotSol(p,0) << "\n"); + UG_LOG("VORHER: modify_GlobalSol rot1Sol(1) = " << m_spCutElementHandler->get_rotSol(p, 1) << "\n"); + } + // set_extraSol(u, transInd, rotInd, 0, p); + number solution; + for (int d = 0; d < dim; ++d) + { + // A. if the linear velocity is given by the user, is needs NOT to be set + if (!m_spCutElementHandler->get_DoF_modus_linear(p)) { + solution = DoFRef(u, transInd[d]); + m_spCutElementHandler->set_extraSolTrans(solution, p, 0, d); + } + + // B. if the angular velocity is given by the user, is needs NOT to be set + if (!m_spCutElementHandler->get_DoF_modus_angular(p)) { + solution = DoFRef(u, rotInd[d]); + m_spCutElementHandler->set_extraSolRot(solution, p, 0, d); + } + + } + if (output ){ + UG_LOG("NACHHER: modify_GlobalSol transSol(0) = " << m_spCutElementHandler->get_transSol(p,0) << "\n"); + UG_LOG("NACHHER: modify_GlobalSol transSol(1) = " << m_spCutElementHandler->get_transSol(p, 1) << "\n"); + UG_LOG("NACHHER: modify_GlobalSol rotSol(0) = " << m_spCutElementHandler->get_rotSol(p,0) << "\n"); + UG_LOG("NACHHER: modify_GlobalSol rot1Sol(1) = " << m_spCutElementHandler->get_rotSol(p, 1) << "\n"); + } + } // end particle loop + +} + +template +void ParticleMapper:: +map_local_data(LocalVector& d) +{ + LocalIndices ind = d.get_indices(); + +// initialize tmp data + LocalVector tmpLocD; + tmpLocD.resize(d.get_indices()); + +// operator= exists only for single value! => set to zero and add + tmpLocD = 0.0; + tmpLocD += d; + +// copy old local vector to new local algebra + for (size_t fct = 0; fct < ind.num_fct(); ++fct) + for (size_t dof = 0; dof < ind.num_dof(fct); ++dof) { + size_t _dof = m_spInterfaceHandlerLocal->corner_orig(dof); + d(fct, dof) = tmpLocD(fct, _dof); + } + +} + +template +void ParticleMapper:: +modify_LocalData(LocalMatrix& locJ, LocalVector& locU, ConstSmartPtr dd) +{ +// INSIDE_DOM: do nothing + if (m_spInterfaceHandlerLocal->elementModus() == INSIDE_DOM) + return; + +// resize local indices: + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE + && m_spInterfaceHandlerLocal->interface_id_all().size() == 2) + { + if (!m_spInterfaceHandlerLocal->StdFV_assembling()) + resize_local_indices(locU, 5); + else + resize_local_indices(locU); + } + else + resize_local_indices(locU); + +// resize local data: + const LocalIndices& ind = m_spInterfaceHandlerLocal->get_local_indices(); + locU.resize(ind); + locJ.resize(ind); + + return; +} + +template +void ParticleMapper:: +modify_LocalData(LocalVectorTimeSeries& uT, LocalMatrix& locJ, LocalVector& locU, + ConstSmartPtr dd) +{ +// INSIDE_DOM: do nothing + if (m_spInterfaceHandlerLocal->elementModus() == INSIDE_DOM) + return; + + for (int i = uT.size() - 1; i >= 0; --i) + { + // resize local indices: + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE + && m_spInterfaceHandlerLocal->interface_id_all().size() == 2) { + if (!m_spInterfaceHandlerLocal->StdFV_assembling()) + resize_local_indices(locU, 5); + else + resize_local_indices(locU); + } else + resize_local_indices(locU); + + // resize local data: + const LocalIndices& ind = + m_spInterfaceHandlerLocal->get_local_indices(); + locU.resize(ind); + locJ.resize(ind); + uT.solution(i).resize(ind); + + } + + return; +} + +template +void ParticleMapper:: +set_QuadriSol(LocalVector& locU, LocalVector& locD) +{ + + if (locU.num_all_dof(0) != 5) + UG_THROW( + "ParticleMapper:set_QuadriSol(): number of DoFs should be 5, but is " << locU.num_all_dof(0) << "\n"); + +// 3. get velocities of the 2 particles: + MathVector transSol1 = m_spCutElementHandler->get_transSol(0, 0); + MathVector rotSol1 = m_spCutElementHandler->get_rotSol(0, 0); + MathVector transSol2 = m_spCutElementHandler->get_transSol(1, 0); + MathVector rotSol2 = m_spCutElementHandler->get_rotSol(1, 0); + +// resize local data as done during 'modify_LocalData' +// --> but there with num_co = 3 for the Triangle with inside node! + for (size_t fct = 0; fct < locU.num_fct() - 1; ++fct) + for (size_t dof = 0; dof < locU.num_all_dof(fct); ++dof) { + MathMatrix rotationMatCo = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co(dof)); + + UG_LOG("radial(" << dof << ": " << m_spInterfaceHandlerLocal->radial_at_co(dof) << "\n"); + + // write solution of particle with prtIndex = 0: + if (dof < 2) { + locU(fct, dof) = transSol1[fct]; + for (int d = 0; d < dim; ++d) + locU(fct, dof) += rotationMatCo[fct][d] * rotSol1[d]; + } + // write solution of particle with prtIndex = 1: + else { + locU(fct, dof) = transSol2[fct]; + for (int d = 0; d < dim; ++d) + locU(fct, dof) += rotationMatCo[fct][d] * rotSol2[d]; + } + } + + m_spInterfaceHandlerLocal->set_QuadriSol(locD, locU); + + UG_LOG("after set_QuadriSol: locU = \n" << locU << "\n"); + UG_LOG("after set_QuadriSol: locD = \n" << locD << "\n"); + + UG_LOG("after set_QuadriSol: m_quadriLocU = \n" << m_spInterfaceHandlerLocal->m_quadriLocU << "\n"); + UG_LOG("after set_QuadriSol: m_quadriLocD = \n" << m_spInterfaceHandlerLocal->m_quadriLocD << "\n"); + +} + +template +void ParticleMapper:: +modify_LocalData(LocalVector& locD, + LocalVector& tmpLocD, LocalVector& locU, + ConstSmartPtr dd) +{ +/////////////////////////////////////////////////////////////////////////////// +// A. INSIDE_DOM: do nothing + if (m_spInterfaceHandlerLocal->elementModus() == INSIDE_DOM) + return; + +/////////////////////////////////////////////////////////////////////////////// +// B. Resize local data: for QUADRILATERALS (2d) or HEXA, PRISM, PYRAMID (3d) + +// resize local indices: + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE + && m_spInterfaceHandlerLocal->interface_id_all().size() == 2) { + if (!m_spInterfaceHandlerLocal->StdFV_assembling()) + resize_local_indices(locU, 5); + else + resize_local_indices(locU); + } else + resize_local_indices(locU); + +// resize local data: + const LocalIndices& ind = m_spInterfaceHandlerLocal->get_local_indices(); + locU.resize(ind); + locD.resize(ind); + tmpLocD.resize(ind); + +/////////////////////////////////////////////////////////////////////////////// +// C. Write the particle rigid body velocity into resized solution 'locU' + + int prtIndex = get_prtIndex(); + const MathVector center = m_spCutElementHandler->get_center(prtIndex); + + MathVector transSol = m_spCutElementHandler->get_transSol(prtIndex,0); + MathVector rotSol = m_spCutElementHandler->get_rotSol(prtIndex, 0); + +// OUTSIDE_DOM: write solution for ALL corners + if (m_spInterfaceHandlerLocal->elementModus() == OUTSIDE_DOM) { + for (size_t fct = 0; fct < locU.num_fct() - 1; ++fct) + for (size_t dof = 0; dof < locU.num_all_dof(fct); ++dof) { + + MathVector radialCo; + VecSubtract(radialCo, m_spInterfaceHandlerLocal->corner(dof), + center); + MathMatrix rotationMatCo = + m_spCutElementHandler->get_rotationMat(radialCo); + + // set solution + locU(fct, dof) = transSol[fct]; + for (int d = 0; d < dim; ++d) + locU(fct, dof) += rotationMatCo[fct][d] * rotSol[d]; + + } + } + +// CUT_BY_INTERFACE: write solution for interface corners + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_INTERFACE + || (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE + && m_spInterfaceHandlerLocal->StdFV_assembling())) { + map_local_data(locU); + + for (size_t fct = 0; fct < locU.num_fct() - 1; ++fct) { + for (size_t i = 0; + i < m_spInterfaceHandlerLocal->interface_id_all().size(); + ++i) { + size_t interfaceID = m_spInterfaceHandlerLocal->interface_id(i); + + MathMatrix rotationMatCo = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co( + interfaceID)); + + // set solution + locU(fct, interfaceID) = transSol[fct]; + + for (int d = 0; d < dim; ++d) + locU(fct, interfaceID) += rotationMatCo[fct][d] * rotSol[d]; + + } + } + } + +// CUT_BY_2_INTERFACE: + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE) { + if (!m_spInterfaceHandlerLocal->StdFV_assembling()) + set_QuadriSol(locU, locD); + } + +} + +template +void ParticleMapper:: +modify_LocalData( + LocalVectorTimeSeries& uT, LocalVector& locD, LocalVector& tmpLocD, + LocalVector& locU, ConstSmartPtr dd, size_t t) +{ + +// 'modify_LocalData()' is called twice (for t=0, t=1) in elem_disc_assemble_util; +// BUT: 'map_local_data()' may be called only once! + if (t == 1) + return; + +/////////////////////////////////////////////////////////////////////////////// +// A. INSIDE_DOM: do nothing + + if (m_spInterfaceHandlerLocal->elementModus() == INSIDE_DOM) + return; + +/////////////////////////////////////////////////////////////////////////////// +// B. Resize local data: for QUADRILATERALS (2d) or HEXA, PRISM, PYRAMID (3d) + +// resize local indices: + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE + && m_spInterfaceHandlerLocal->interface_id_all().size() == 2) { + if (!m_spInterfaceHandlerLocal->StdFV_assembling()) + resize_local_indices(locU, 5); + else + resize_local_indices(locU); + } else + resize_local_indices(locU); + +// resize local data: + const LocalIndices& ind = m_spInterfaceHandlerLocal->get_local_indices(); + locU.resize(ind); + locD.resize(ind); + tmpLocD.resize(ind); + +/////////////////////////////////////////////////////////////////////////////// +// C. Write the particle rigid body velocity into resized solution 'locU' + + int prtIndex = get_prtIndex(); + const MathVector center = m_spCutElementHandler->get_center(prtIndex); + +// loop all time points and assemble them + for (int i = uT.size() - 1; i >= 0; --i) { + + uT.solution(i).resize(ind); + + LocalVector* vecMod = &uT.solution(i); + + MathVector transSol = m_spCutElementHandler->get_transSol(prtIndex, i); + MathVector rotSol = m_spCutElementHandler->get_rotSol(prtIndex,i); + + // OUTSIDE_DOM: write solution for ALL corners + if (m_spInterfaceHandlerLocal->elementModus() == OUTSIDE_DOM) { + for (size_t fct = 0; fct < locU.num_fct() - 1; ++fct) + for (size_t dof = 0; dof < locU.num_all_dof(fct); ++dof) { + MathVector radialCo; + VecSubtract(radialCo, + m_spInterfaceHandlerLocal->corner(dof), center); + MathMatrix rotationMatCo = + m_spCutElementHandler->get_rotationMat( + radialCo); + + // set solution + (*vecMod)(fct, dof) = transSol[fct]; + for (int d = 0; d < dim; ++d) + (*vecMod)(fct, dof) += rotationMatCo[fct][d] + * rotSol[d]; + + } + } + + // CUT_BY_INTERFACE: write solution for interface corners + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_INTERFACE + || (m_spInterfaceHandlerLocal->elementModus() + == CUT_BY_2_INTERFACE + && m_spInterfaceHandlerLocal->StdFV_assembling())) { + map_local_data(*vecMod); + + for (size_t fct = 0; fct < locU.num_fct() - 1; ++fct) { + for (size_t i = 0; + i < m_spInterfaceHandlerLocal->interface_id_all().size(); + ++i) { + size_t interfaceID = + m_spInterfaceHandlerLocal->interface_id(i); + MathMatrix rotationMatCo = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co( + interfaceID)); + + // set solution + (*vecMod)(fct, interfaceID) = transSol[fct]; + for (int d = 0; d < dim; ++d) + (*vecMod)(fct, interfaceID) += rotationMatCo[fct][d] + * rotSol[d]; + } + } + + } + + // CUT_BY_2_INTERFACE: + if (m_spInterfaceHandlerLocal->elementModus() == CUT_BY_2_INTERFACE) { + if (!m_spInterfaceHandlerLocal->StdFV_assembling()) + UG_THROW("modify_LocalData(): CUT_BY_2_INTERFACE\n"); //set_QuadriSol(locU, locD); + } + + } // end time loop + +} + + +template +void ParticleMapper:: +set_identity_mat_constraint(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd) +{ +// get levIndex + const int levIndex = get_Index(dd->grid_level(), dd); + +// get local indices + const LocalIndices& rowInd = lmat.get_row_indices(); + + for (size_t fct1 = 0; fct1 < lmat.num_all_row_fct(); ++fct1) + for (size_t dof1 = 0; dof1 < lmat.num_all_row_dof(fct1); ++dof1) { + const size_t _dof1 = m_spInterfaceHandlerLocal->corner_orig(dof1); + const size_t rowIndex = rowInd.index(fct1, _dof1); + const size_t rowComp = rowInd.comp(fct1, _dof1); + + // m_spCutElementHandler->is_extraDoF returns false, if particle velocity is given by the model + bool is_extraDoF = m_spCutElementHandler->is_extraDoF( + DoFIndex(rowIndex, rowComp), levIndex); + + // IFF node is no transInd/rotInd-node and no interface node! + bool is_interfaceDoF = true; + // on interface, only pressure is real DoF: + if (fct1 != dim) + is_interfaceDoF = false; + else if (m_spInterfaceHandlerLocal->StdFV_assembling()) { + + is_interfaceDoF = + m_spInterfaceHandlerLocal->remapped_fromInterface( + _dof1); + //is_interfaceDoF = true; //m_spInterfaceHandlerLocal->lies_onInterface(_dof1); + + } else + is_interfaceDoF = + m_spInterfaceHandlerLocal->remapped_fromInterface( + _dof1); + + // DoFs on interface OR for translational/angular velocity need to remain free + if (is_interfaceDoF || is_extraDoF) + continue; + else + BlockRef(mat(rowIndex, rowIndex), rowComp, rowComp) = 1.0; + } + +} + +template +void ParticleMapper:: +add_local_mat_to_global(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd) +{ +// reset print modus for cut element data to 'false' +// => only printed during assemling of defect, i.e. ONE loop over all element + m_spInterfaceHandlerLocal->set_print_cutElemData(false); + + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + LocalMatrix locJ = lmat; + + switch (modus) { + case OUTSIDE_DOM: // no DoFs: set identity-rows + set_identity_mat_constraint(mat, lmat, dd); //IFF element does NOT include transInd/rotInd-node! + break; + case INSIDE_DOM: // call usual local-to-global-mapping + AddLocalMatrixToGlobal(mat, lmat); + break; + case CUT_BY_INTERFACE: // call adapted local-to-global-mapping + add_local_mat_to_global_interface(mat, lmat, dd); + break; + case CUT_BY_2_INTERFACE: // call adapted local-to-global-mapping + add_local_mat_to_global_interface_for2(mat, lmat, dd); + break; + default: + throw(UGError("Error in IInterfaceMapper::add_local_mat_to_global()!")); + + } + +} + +template +void ParticleMapper:: +add_local_vec_to_global(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd) +{ + + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + + switch (modus) { + case OUTSIDE_DOM: // no DoFs: no contribution to defect + break; + case INSIDE_DOM: // call usual local-to-global-mapping + AddLocalVector(vec, lvec); + break; + case CUT_BY_INTERFACE: // call adapted local-to-global-mapping + add_local_vec_to_global_interface(vec, lvec, dd); + break; + case CUT_BY_2_INTERFACE: // call adapted local-to-global-mapping + add_local_vec_to_global_interface_for2(vec, lvec, dd); + break; + default: + throw(UGError("Error in IInterfaceMapper::add_local_vec_to_global()!")); + + } + +} + + +template +void ParticleMapper:: +add_mass_part_jac(matrix_type& mat, std::vector transInd, std::vector rotInd, + const int levIndex, const int prtIndex) +{ + number volume; + + if (m_bVolumeCompExact) + volume = Volume(levIndex, prtIndex); + else + volume = compute_volume(levIndex, prtIndex); + +// add mass term to global matrix (instead of using IConstraint::adjust_jacobian() ) + for (int d = 0; d < dim; ++d) { + DoFRef(mat, transInd[d], transInd[d]) += m_spInterfaceHandlerLocal->get_density(prtIndex)*volume; + // for dim = 2: the second DoF of rotInd is unused! + if (dim == 3 || d == 0) + DoFRef(mat, rotInd[d], rotInd[d]) += MomOfInertia(levIndex, prtIndex, volume); + } + +// adding mass/inertia terms only ONCE!! + if (m_bVolumeCompExact) + m_bFlagInertial[prtIndex] = false; + +} + + + +// call for 'CUT_BY_2_INTERFACE' +template +void ParticleMapper:: +add_local_mat_to_global_interface_for2(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd) +{ +// REMARK: not finally tested! + UG_THROW("ParticleMapper::add_local_mat_to_global_interface_for2(): not finally tested!!!!\n"); + +// get data + const int levIndex = get_Index(dd->grid_level(), dd); + std::vector < DoFIndex > transInd1, rotInd1, transInd2, rotInd2; + m_spCutElementHandler->get_global_indices(transInd1, rotInd1, levIndex,0); + m_spCutElementHandler->get_global_indices(transInd2, rotInd2, levIndex,1); + + + if (!UsualAss()) + { + if (m_spInterfaceHandlerLocal->StdFV_assembling()) + add_local_mat_to_global_FT_for2_StdFV(mat, lmat, transInd1, rotInd1, + transInd2, rotInd2); + else + add_local_mat_to_global_FT_for2(mat, lmat, transInd1, rotInd1, + transInd2, rotInd2); + } + else { + UG_THROW("add_local_mat_to_global_interface_for2(): -> not implemented!\n"); + } +} + +// call for 'CUT_BY_INTERFACE' +template +void ParticleMapper:: +add_local_mat_to_global_interface(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd) +{ +// get data + const int levIndex = get_Index(dd->grid_level(), dd); + int prtIndex = get_prtIndex(); + + std::vector < DoFIndex > transInd, rotInd; + m_spCutElementHandler->get_global_indices(transInd, rotInd, levIndex, prtIndex); + +// A. add mass part to jacobian + if (m_bFlagInertial[prtIndex]) + add_mass_part_jac(mat, transInd, rotInd, levIndex, prtIndex); + +// B. add local to global + if (!UsualAss()){ + add_local_mat_to_global_FT(mat, lmat, transInd, rotInd); + } + else { + UG_THROW("ParticleMapper::add_local_mat_to_global_usual(): -> not implemented!\n"); + } + +} + + + +template +void ParticleMapper:: +add_glowinski_repulsive_force_rhs(vector_type& vec, std::vector transInd, + std::vector rotInd,const int levIndex, const int prtIndex) +{ +//////////////////////////////////////////////////////////////// +// compute repulsive force: +//////////////////////////////////////////////////////////////// + + for (size_t i = 0; i < num_particles(); ++i) { + if ((int)i == prtIndex) {continue;} + // Get particle values + MathVector center_i = m_spCutElementHandler->get_center(i); + MathVector center_prtIndex = m_spCutElementHandler->get_center(prtIndex); + double Mass_i = Mass(levIndex,i); + double Mass_prtIndex = Mass(levIndex,prtIndex); + + // Calculate distance vector between center points + double dist = 0; + for (size_t d = 0; d < dim; ++d){ + dist += (center_i[d]-center_prtIndex[d])*(center_i[d]-center_prtIndex[d]); + } + dist = sqrt(dist); + MathVector distanceVec = center_prtIndex; + distanceVec -= center_i; + distanceVec /= dist; + + // Calculate force + double force = (Mass_i+Mass_prtIndex)/2*981/(0.5*m_epsilon); + double max1 = std::max(0.0,-(dist-m_spCutElementHandler->get_radius(i)-m_spCutElementHandler->get_radius(prtIndex)-m_rho)/(m_rho)); + force *= pow(max1,2); + + // Scale with time step + number timeScale = 1.0; + if (is_time_dependent()) { + timeScale = m_dt; + force *= timeScale; + } + + // UG_LOG("Max/original force is " << force << ".\n"); + + UG_LOG("Unscaled Max/original repulsive force is "< +void ParticleMapper:: +add_minimum_correction_force_rhs(vector_type& vec,std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex) +{ +//////////////////////////////////////////////////////////////// +// compute repulsive force: +//////////////////////////////////////////////////////////////// + for (size_t i = 0; i < num_particles(); ++i) { + if (!m_bGlowRepulsiveForce && (i != prtIndex)) { + + if (i == prtIndex) {continue;} + // Get particle values + MathVector center_i = m_spCutElementHandler->get_center(i); + MathVector center_prtIndex = m_spCutElementHandler->get_center(prtIndex); + double Mass_i = Mass(levIndex,i); + double Mass_prtIndex = Mass(levIndex,prtIndex); + + // Calculate distance vector between center points + double dist = 0; + for (size_t d = 0; d < dim; ++d){ + dist += (center_i[d]-center_prtIndex[d])*(center_i[d]-center_prtIndex[d]); + } + dist = sqrt(dist); + MathVector distanceVec = center_prtIndex; + distanceVec -= center_i; + distanceVec /= dist; + if (m_bForceLog) { + if (dim == 2) { + UG_LOG("distanceVec is ("<< distanceVec[0] <<","<< distanceVec[1] <<").\n"); + } else { + UG_LOG("distanceVec is ("<< distanceVec[0] <<","<< distanceVec[1] <<","<< distanceVec[2] <<").\n"); + } + } + + // Calculate force + double force = (Mass_i+Mass_prtIndex)/2*m_gravityConst/(m_epsilon); + double max1 = std::max(0.0,-(dist-m_spCutElementHandler->get_radius(i)-m_spCutElementHandler->get_radius(prtIndex)-m_rho)/(m_rho)); + force *= pow(max1,2); + + // Scale with time step + number timeScale = 1.0; + if (is_time_dependent()) { + timeScale = m_dt; + force *= timeScale; + } + + if (m_bForceLog) { + UG_LOG("Unscaled Max/original repulsive force is "< Delta_r = m_repulsiveDistance[prtIndex]; + MathVector force; + VecScale(force, Delta_r, 1/(m_dt*m_dt)); + + DoFRef(vec, transInd[0]) -= Mass_prtIndex*force[0]; + DoFRef(vec, transInd[1]) -= Mass_prtIndex*force[1]; + if (dim == 3) + DoFRef(vec, transInd[2]) -= Mass_prtIndex*force[2]; + if (m_bForceLog) { + UG_LOG("Unscaled equivalent repulsive force is "< +void ParticleMapper:: +add_repulsive_force_rhs(vector_type& vec, std::vector transInd, + std::vector rotInd, const int levIndex, const int prtIndex) +{ + //////////////////////////////////////////////////////////////// + // compute repulsive force: + //////////////////////////////////////////////////////////////// + + double force = m_repulsiveForce; + + for (size_t i = 0; i < num_particles(); ++i) { + if ((int)i == prtIndex) {continue;} + // Get particle values + MathVector center_i = m_spCutElementHandler->get_center(i); + MathVector center_prtIndex = m_spCutElementHandler->get_center(prtIndex); + double Mass_prtIndex = Mass(levIndex,prtIndex); + + // Calculate distance vector between center points + double dist = 0; + for (size_t d = 0; d < dim; ++d){ + dist += (center_i[d]-center_prtIndex[d])*(center_i[d]-center_prtIndex[d]); + } + dist = sqrt(dist); + MathVector distanceVec = center_prtIndex; + distanceVec -= center_i; + distanceVec /= dist; + + UG_LOG("Unscaled equivalent repulsive force is "< +void ParticleMapper:: +set_gravitational_rhs(vector_type& vec, std::vector transInd, std::vector rotInd, + const int levIndex, const int prtIndex) +{ + bool logGravity = false; + +// some initial checks: Parameters need to be set via lua-script! + if (m_gravityConst == 0.0) + UG_THROW("m_gravityConst not defined!\n"); + if (is_time_dependent() && m_dt == 0.0) + UG_THROW("time step 'm_dt' not defined! Needs to be set via lua-script: 'set_time_step(dt)'...\n"); + + if (logGravity) + { + UG_LOG("//////////////////////////////// - log_Gravity (" << prtIndex << ") - ///////////////////////////////\n"); + + UG_LOG("VORHER: defect(trans,0): " << DoFRef(vec, transInd[0]) << "\n"); + UG_LOG("VORHER: defect(trans,1): " << DoFRef(vec, transInd[1]) << "\n"); + if (dim == 3) + UG_LOG("VORHER: defect(trans,2): " << DoFRef(vec, transInd[2]) << "\n"); + + UG_LOG("VORHER: defect(rot,0): " << DoFRef(vec, rotInd[0]) << "\n"); + UG_LOG("VORHER: defect(rot,1): " << DoFRef(vec, rotInd[1]) << "\n"); + if (dim == 3) + UG_LOG("VORHER: defect(rot,2): " << DoFRef(vec, rotInd[2]) << "\n"); + + UG_LOG("m_gravityConst = " << m_gravityConst << "\n"); + + } + + //////////////////////////////////////////////////////////////// + // compute gravitational force to rhs: + //////////////////////////////////////////////////////////////// + + number volume; + if (m_bVolumeCompExact) + volume = Volume(levIndex, prtIndex); + else + volume = compute_volume(levIndex, prtIndex); + + const number gravitationalMass = Mass(levIndex, prtIndex, volume); + + number gravityForce = m_gravityConst * gravitationalMass; + if (dim == 3) + gravityForce = m_gravityConst * gravitationalMass; + + number timeScale = 1.0; + if (is_time_dependent()) + timeScale = m_dt; + + //////////////////////////////////////////////////////////////// + // add gravitational force to rhs: + //////////////////////////////////////////////////////////////// + if (dim == 2) + DoFRef(vec, transInd[0]) -= timeScale * gravityForce; + if (dim == 3) + DoFRef(vec, transInd[2]) -= timeScale * gravityForce; + + + if (logGravity) + { + UG_LOG("gravitationalMass: " << gravitationalMass << "\n"); + UG_LOG("gravForce added: " << gravityForce << "dt: " << m_dt <<"\n"); + UG_LOG("//////////////////////////////// - log_Gravity - ///////////////////////////////\n"); + UG_LOG("NACHHER: defect(trans,0): " << DoFRef(vec, transInd[0]) << "\n"); + UG_LOG("NACHHER: defect(trans,1): " << DoFRef(vec, transInd[1]) << "\n"); + if (dim == 3) + UG_LOG("NACCHHER: defect(trans,2): " << DoFRef(vec, transInd[2]) << "\n"); + UG_LOG("NACHHER: defect(rot,0): " << DoFRef(vec, rotInd[0]) << "\n"); + UG_LOG("NACHHER: defect(rot,1): " << DoFRef(vec, rotInd[1]) << "\n"); + if (dim == 3) + UG_LOG("NACHHER: defect(rot,2): " << DoFRef(vec, rotInd[2]) << "\n"); + } + +} + + +template +void ParticleMapper:: +add_mass_part_def(vector_type& vec,std::vector transInd, std::vector rotInd, + const int levIndex, const int prtIndex) +{ + bool logGravity = false; + if (logGravity) + { + UG_LOG("//////////////////////////////// - log momentum - ///////////////////////////////\n"); + UG_LOG("VORHER: defect(trans,0): " << DoFRef(vec, transInd[0]) << "\n"); + UG_LOG("VORHER: defect(trans,1): " << DoFRef(vec, transInd[1]) << "\n"); + UG_LOG("VORHER: defect(rot,0): " << DoFRef(vec, rotInd[0]) << "\n"); + UG_LOG("VORHER: defect(rot,1): " << DoFRef(vec, rotInd[1]) << "\n"); + } + + number volume; + if (m_bVolumeCompExact) // compute volume analytically + volume = Volume(levIndex, prtIndex); + else // compute volume by adding volumes of elements, covered by the prtIndex-th particle + volume = compute_volume(levIndex, prtIndex); + + const number mass = m_spInterfaceHandlerLocal->get_density(prtIndex)*volume; + const number momOfInertia = MomOfInertia(levIndex, prtIndex, volume); + MathVector < 2 > vScaleMass; + vScaleMass[0] = 1.0; + vScaleMass[1] = -1.0; + +// loop all time points and add mass parts to global defect + for (int i = 1; i >= 0; --i) + { + if (logGravity) + UG_LOG("i = " << i << ":\n"); + + for (int d = 0; d < dim; ++d) { + double transSol = m_spCutElementHandler->get_transSol(prtIndex,i)[d]; + double rotSol = m_spCutElementHandler->get_rotSol(prtIndex, i)[d]; + + DoFRef(vec, transInd[d]) += vScaleMass[i] * mass * transSol; + if (dim == 3 || d == 0) + DoFRef(vec, rotInd[d]) += vScaleMass[i] * momOfInertia * rotSol; + + if (logGravity) + { + UG_LOG("transSol(" << i << ") = " << transSol << "\n"); + UG_LOG("rotSol(" << i << ") = " << rotSol << "\n"); + UG_LOG("+= vScaleMass[" << i << "]*mass*transSol = " << vScaleMass[i]*mass*transSol << "\n"); + UG_LOG("+= vScaleMass[" << i << "]*momOfInertia*rotSol = " << vScaleMass[i]*momOfInertia*rotSol << "\n"); + } + } + } + + if (logGravity) + { + UG_LOG("NACHHER: defect(trans,0): " << DoFRef(vec, transInd[0]) << "\n"); + UG_LOG("NACCHHER: defect(trans,1): " << DoFRef(vec, transInd[1]) << "\n"); + UG_LOG("NACHHER: defect(rot,0): " << DoFRef(vec, rotInd[0]) << "\n"); + UG_LOG("NACHHER: defect(rot,1): " << DoFRef(vec, rotInd[1]) << "\n"); + } + +// call only ONCE!! (during 'modify_GlobalSol(): m_bFlagInertial := true) + if (m_bVolumeCompExact) + m_bFlagInertial[prtIndex] = false; + + + return; + +} + +template +void ParticleMapper:: +add_rhs(vector_type& vec, std::vector transInd, std::vector rotInd, + const int levIndex, const int prtIndex) +{ + +// call assembling of gravitational force acting on prtIndex-th particle + if (m_bFlagGravity[prtIndex]){ + set_gravitational_rhs(vec, transInd, rotInd, levIndex, prtIndex); + } + +// call assembling of repuslive force acting on prtIndex-th particle + if (m_bRepulsiveForce && m_bFlagGravity[prtIndex]){ + add_repulsive_force_rhs(vec, transInd, rotInd, levIndex, prtIndex); + } + +// call assembling of glowinski repuslive force acting on prtIndex-th particle + if (m_bGlowRepulsiveForce && m_bFlagGravity[prtIndex]){ + add_glowinski_repulsive_force_rhs(vec, transInd, rotInd, levIndex, prtIndex); + } + +// no additional if here because of calculations and a UG_LOG call inside the function that should be printed everytime. +// if clause is inside the function and force is added only if m_bMaxRepulsiveForce + if (m_bFlagGravity[prtIndex]) { + add_minimum_correction_force_rhs(vec, transInd, rotInd, levIndex, prtIndex); + } + + +// call this method only ONCE!! (during 'modify_GlobalSol(): m_bFlagGravity := true) + if (m_bVolumeCompExact) + m_bFlagGravity[prtIndex] = false; + +} + +// call for 'CUT_BY_2_INTERFACE' +template +void ParticleMapper:: +add_local_vec_to_global_interface_for2(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd) +{ + +// REMARK: not finally tested! + UG_THROW("ParticleMapper::add_local_vec_to_global_interface_for2(): not finally tested!!!!\n"); + +// get data + const int levIndex = get_Index(dd->grid_level(), dd); + std::vector < DoFIndex > transInd1, rotInd1, transInd2, rotInd2; + m_spCutElementHandler->get_global_indices(transInd1, rotInd1, levIndex,0); + m_spCutElementHandler->get_global_indices(transInd2, rotInd2, levIndex,1); + +// A. add local to global + if (!UsualAss()) + if (m_spInterfaceHandlerLocal->StdFV_assembling()) + add_local_vec_to_global_FT_for2_StdFV(vec, lvec, transInd1, rotInd1, + transInd2, rotInd2); + else + add_local_vec_to_global_FT_for2(vec, lvec, transInd1, rotInd1, + transInd2, rotInd2); + else + UG_THROW("add_local_vec_to_global_interface_for2(): -> not implemented!\n"); + +} + +// call for 'CUT_BY_INTERFACE' +template +void ParticleMapper:: +add_local_vec_to_global_interface(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd) +{ +// get data + const int levIndex = get_Index(dd->grid_level(), dd); + const int prtIndex = get_prtIndex(); + std::vector < DoFIndex > transInd, rotInd; + m_spCutElementHandler->get_global_indices(transInd, rotInd, levIndex, + prtIndex); + +// A. add local to global + if (!UsualAss()){ + add_local_vec_to_global_FT(vec, lvec, transInd, rotInd); + } + else { + UG_THROW("in ParticleMapper::add_local_vec_to_global_interface(): !UsualAss() not implemented!\n"); + } + +// B. add mass part to defect + if (m_bFlagInertial[prtIndex]) + add_mass_part_def(vec, transInd, rotInd, levIndex, prtIndex); + +// C. add rhs to defect + if (m_bFlagGravity[prtIndex]) + add_rhs(vec, transInd, rotInd, levIndex, prtIndex); + +} + + +template +size_t ParticleMapper:: +map_for2(size_t dof) +{ + UG_THROW("ParticleMapper::map_for2(): not finally tested!\n"); + + if (dof == 4) + return m_spInterfaceHandlerLocal->m_vNOInterfaceID[0]; + else if (dof < 2) + return m_spInterfaceHandlerLocal->m_vQuadriOrigID[dof]; + else if (dof >= 2) + return m_spInterfaceHandlerLocal->m_vQuadriOrigID[dof]; + else + UG_THROW("ParticleMapper::map_for2(): error: dof = " + << dof << " should lie between 0 and 4!\n"); + +} + +template +void ParticleMapper:: +add_local_mat_to_global_FT_for2_StdFV( + matrix_type& mat, const LocalMatrix& lmat, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2) +{ +// REMARK: not finally tested! + UG_THROW("ParticleMapper::add_local_mat_to_global_FT_for2_StdFV(): not finally tested!!!!\n"); + + MathMatrix rotationMatCo, rotationMatIP, rotationMatIP_transposed; + MathVector radialVector; + + const LocalIndices& rowInd = lmat.get_row_indices(); + const LocalIndices& colInd = lmat.get_col_indices(); + + bool remap = true; + + for (size_t fct1 = 0; fct1 < lmat.num_all_row_fct(); ++fct1) + for (size_t dof1 = 0; dof1 < lmat.num_all_row_dof(fct1); ++dof1) { + size_t _dof1 = dof1; + if (remap) + _dof1 = m_spInterfaceHandlerLocal->corner_orig(dof1); + const DoFIndex indexRow = DoFIndex(rowInd.index(fct1, _dof1), + rowInd.comp(fct1, _dof1)); + + for (size_t fct2 = 0; fct2 < lmat.num_all_col_fct(); ++fct2) + for (size_t dof2 = 0; dof2 < lmat.num_all_col_dof(fct2); + ++dof2) { + size_t _dof2 = dof2; + if (remap) + _dof2 = m_spInterfaceHandlerLocal->corner_orig(dof2); + const DoFIndex indexCol = DoFIndex( + colInd.index(fct2, _dof2), + colInd.comp(fct2, _dof2)); + + + size_t modus = getMappingModus(fct1, _dof1, fct2, _dof2); + + rotationMatCo = m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co(dof2)); + rotationMatIP = m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_ip(dof1)); + + const int prtIndex1 = getPrtIndex(_dof1); + const int prtIndex2 = getPrtIndex(_dof2); + + switch (modus) { + case 0: { + DoFRef(mat, indexRow, indexCol) += lmat.value(fct1, + dof1, fct2, dof2); + } + break; + case 1: { + std::vector < DoFIndex > transInd_to, rotInd_to; + // coupling TO particle1 + if (prtIndex2 == 0) { + transInd_to = transInd1; + rotInd_to = rotInd1; + } + // coupling TO particle2 + else { + transInd_to = transInd2; + rotInd_to = rotInd2; + } + + DoFRef(mat, indexRow, transInd_to[fct2]) += lmat.value( + fct1, dof1, fct2, dof2); + + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, indexRow, rotInd_to[cmp]) += lmat.value( + fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp]; + + } + break; + case 2: { + std::vector < DoFIndex > transInd_from, rotInd_from; + // coupling FROM particle1 + if (prtIndex1 == 0) { + transInd_from = transInd1; + rotInd_from = rotInd1; + } + // coupling FROM particle2 + else { + transInd_from = transInd2; + rotInd_from = rotInd2; + } + + DoFRef(mat, transInd_from[fct1], indexCol) += + lmat.value(fct1, dof1, fct2, dof2); + + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, rotInd_from[cmp], indexCol) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatIP[fct1][cmp]; + + } + break; + case 3: { + std::vector < DoFIndex > transInd_from, rotInd_from; + std::vector < DoFIndex > transInd_to, rotInd_to; + // coupling TO particle1 + if (prtIndex2 == 0) { + transInd_to = transInd1; + rotInd_to = rotInd1; + } + // coupling TO particle2 + else { + transInd_to = transInd2; + rotInd_to = rotInd2; + } + + // coupling FROM particle1 + if (prtIndex1 == 0) { + transInd_from = transInd1; + rotInd_from = rotInd1; + } + // coupling FROM particle2 + else { + transInd_from = transInd2; + rotInd_from = rotInd2; + } + + // trans-trans + DoFRef(mat, transInd_from[fct1], transInd_to[fct2]) += + lmat.value(fct1, dof1, fct2, dof2); + + // trans-rot ( w x r = w x r_co = R_co^T*w); (R^T*... <=> [fct1][cmp]) + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, transInd_from[fct1], rotInd_to[cmp]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp]; + + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, rotInd_from[cmp], transInd_to[fct2]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatIP[fct1][cmp]; + + for (size_t cmp1 = 0; cmp1 < dim; ++cmp1) + for (size_t cmp2 = 0; cmp2 < dim; ++cmp2) + DoFRef(mat, rotInd_from[cmp1], rotInd_to[cmp2]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp2] + * rotationMatIP[fct1][cmp1]; + + } + break; + case 4: { // to pressure outside: sum = e-20 => skip! + } + break; + + default: + throw(UGError("Error in LocToGlob!")); + + } // end switch-cases + } // end dof2-loop + + } + +} + +template +void ParticleMapper:: +add_local_mat_to_global_FT_for2( + matrix_type& mat, const LocalMatrix& lmat, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2) +{ +// REMARK: not finally tested! + UG_THROW("ParticleMapper::add_local_mat_to_global_FT_for2(): not finally tested!!!!\n"); + + MathMatrix rotationMatCo, rotationMatIP, rotationMatIP_transposed; + MathVector radialVector; + + const LocalIndices& rowInd = lmat.get_row_indices(); + const LocalIndices& colInd = lmat.get_col_indices(); + + for (size_t fct1 = 0; fct1 < lmat.num_all_row_fct(); ++fct1) + for (size_t dof1 = 0; dof1 < lmat.num_all_row_dof(fct1); ++dof1) { + const size_t dofMap1 = map_for2(dof1); + const DoFIndex indexRow = DoFIndex(rowInd.index(fct1, dofMap1), + rowInd.comp(fct1, dofMap1)); + + for (size_t fct2 = 0; fct2 < lmat.num_all_col_fct(); ++fct2) + for (size_t dof2 = 0; dof2 < lmat.num_all_col_dof(fct2); + ++dof2) { + const size_t dofMap2 = map_for2(dof2); + const DoFIndex indexCol = DoFIndex( + colInd.index(fct2, dofMap2), + colInd.comp(fct2, dofMap2)); + + rotationMatCo = m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co(dof2)); + rotationMatIP = m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_ip(dof1)); + + size_t modus = getMappingModus_for2(fct1, dof1, fct2, dof2); + + switch (modus) { + case 0: { + DoFRef(mat, indexRow, indexCol) += lmat.value(fct1, + dof1, fct2, dof2); + } + break; + case 1: { + std::vector < DoFIndex > transInd_to, rotInd_to; + // coupling TO particle1 + if (dof2 < 2) { + transInd_to = transInd1; + rotInd_to = rotInd1; + } + // coupling TO particle2 + else { + transInd_to = transInd2; + rotInd_to = rotInd2; + } + + DoFRef(mat, indexRow, transInd_to[fct2]) += lmat.value( + fct1, dof1, fct2, dof2); + + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, indexRow, rotInd_to[cmp]) += lmat.value( + fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp]; + + } + break; + case 2: { + std::vector < DoFIndex > transInd_from, rotInd_from; + // coupling FROM particle1 + if (dof1 < 2) { + transInd_from = transInd1; + rotInd_from = rotInd1; + } + // coupling FROM particle2 + else { + transInd_from = transInd2; + rotInd_from = rotInd2; + } + UG_LOG("transInd_from: " << transInd_from[0] << "\n"); + UG_LOG("rotInd_from: " << rotInd_from[0] << "\n"); + + DoFRef(mat, transInd_from[fct1], indexCol) += + lmat.value(fct1, dof1, fct2, dof2); + + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, rotInd_from[cmp], indexCol) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatIP[fct1][cmp]; + + } + break; + case 3: { + std::vector < DoFIndex > transInd_from, rotInd_from; + std::vector < DoFIndex > transInd_to, rotInd_to; + // coupling TO particle1 + if (dof2 < 2) { + transInd_to = transInd1; + rotInd_to = rotInd1; + } + // coupling TO particle2 + else { + transInd_to = transInd2; + rotInd_to = rotInd2; + } + UG_LOG("transInd_to: " << transInd_to[0] << "\n"); + UG_LOG("rotInd_to: " << rotInd_to[0] << "\n"); + + // coupling FROM particle1 + if (dof1 < 2) { + transInd_from = transInd1; + rotInd_from = rotInd1; + } + // coupling FROM particle2 + else { + transInd_from = transInd2; + rotInd_from = rotInd2; + } + UG_LOG("transInd_from: " << transInd_from[0] << "\n"); + UG_LOG("rotInd_from: " << rotInd_from[0] << "\n"); + + // trans-trans + DoFRef(mat, transInd_from[fct1], transInd_to[fct2]) += + lmat.value(fct1, dof1, fct2, dof2); + + // trans-rot ( w x r = w x r_co = R_co^T*w); (R^T*... <=> [fct1][cmp]) + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, transInd_from[fct1], rotInd_to[cmp]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp]; + + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, rotInd_from[cmp], transInd_to[fct2]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatIP[fct1][cmp]; + + for (size_t cmp1 = 0; cmp1 < dim; ++cmp1) + for (size_t cmp2 = 0; cmp2 < dim; ++cmp2) + DoFRef(mat, rotInd_from[cmp1], rotInd_to[cmp2]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp2] + * rotationMatIP[fct1][cmp1]; + + } + break; + case 4: { // to pressure outside: sum = e-20 => skip! + } + break; + + default: + throw(UGError("Error in LocToGlob!")); + + } // end switch-cases + + } // end dof2-loop + + //////////////////////////////////////////////////////////////////////////////////////////////// + // finally set dirichlet row for velocity DoFs on interface: + // ---> also done during 'set_identity_mat_constraint()', BUT only for OUTSIDE_DOM-elements!! + //////////////////////////////////////////////////////////////////////////////////////////////// + + // dof1 != 4 => DoF lies on an interface of one of the two particles + if (dof1 != 4) { + const size_t indexDiagInd = rowInd.index(0, dofMap1); + if (indexDiagInd != transInd1[0][0] + && indexDiagInd != rotInd1[0][0] + && indexDiagInd != transInd2[0][0] + && indexDiagInd != rotInd2[0][0]) { + for (size_t d = 0; d < dim; ++d) { + const DoFIndex indexDiag = DoFIndex( + rowInd.index(d, dofMap1), + rowInd.comp(d, dofMap1)); + DoFRef(mat, indexDiag, indexDiag) = 1.0; + } + } + } + } // end dof1-loop + + +} + +template +void ParticleMapper:: +add_local_mat_to_global_FT(matrix_type& mat, const LocalMatrix& lmat, + std::vector transInd, std::vector rotInd) +{ + bool bOld = true; + bool remap = true; + bool transposed = false; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ATTENTION: lmat has the algebra-structure and size of m_locJ (i.e. bigger than standard, in case of QUADIR) +// BUT: the indices-infos are still the original one for the associated global indices: +// --> lmat.m_pRowIndex, lmat.m_pColIndex +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + LocalMatrix locMatOrig = lmat; + LocalMatrix locMatTrans = lmat; + LocalMatrix locMatRot = lmat; + locMatOrig = 0.0; + locMatTrans = 0.0; + locMatRot = 0.0; + +// cutElem = true: => re-associate to trans/rot-DoFs + //////////////////////////////////////////////////////////////////////////////////////////////////// + // for LocToGLob-mapping only the following geo-data are needed: + // geo.vOriginalCornerID, geo.vInterfaceID, geo.m_bUseOriginalCornerRadial, + // geo.m_vRadialAtCo, geo.m_vRadialAtIP + //////////////////////////////////////////////////////////////////////////////////////////////////// + + MathMatrix rotationMatCo, rotationMatIP; + MathVector radialVector; + + const LocalIndices& rowInd = lmat.get_row_indices(); + const LocalIndices& colInd = lmat.get_col_indices(); + + + const int prtIndex = get_prtIndex(); + + if (prtIndex == -1) + UG_THROW("ParticleMapper::add_local_mat_to_global_FT(): prtIndex invalid!: prtIndex = " << prtIndex << "\n"); + + for (size_t fct1 = 0; fct1 < lmat.num_all_row_fct(); ++fct1) + for (size_t dof1 = 0; dof1 < lmat.num_all_row_dof(fct1); ++dof1) { + size_t _dof1 = dof1; + if (remap) + _dof1 = m_spInterfaceHandlerLocal->corner_orig(dof1); + + const DoFIndex indexRow = DoFIndex(rowInd.index(fct1, _dof1), + rowInd.comp(fct1, _dof1)); + + for (size_t fct2 = 0; fct2 < lmat.num_all_col_fct(); ++fct2) + for (size_t dof2 = 0; dof2 < lmat.num_all_col_dof(fct2); + ++dof2) + { + + size_t _dof2 = dof2; + if (remap) + _dof2 = m_spInterfaceHandlerLocal->corner_orig(dof2); + const DoFIndex indexCol = DoFIndex( + colInd.index(fct2, _dof2), + colInd.comp(fct2, _dof2)); + + size_t modus = getMappingModus(fct1, _dof1, fct2, _dof2); + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // compute according rotational matrices: + // --> the couplings to/from the angular velocity need to be multiplied by + // the matrix resutling from the cross product + if (1) //or: if ( m_spInterfaceHandlerLocal->m_bUseOriginalCornerRadial ) + { + rotationMatCo = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co( + dof2)); + rotationMatIP = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_ip( + dof1)); + } else { + rotationMatCo = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_co( + dof2)); + rotationMatIP = + m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_ip( + dof1)); + } + + MathMatrix rotationMatIP_transposed; + Transpose(rotationMatIP_transposed, rotationMatIP); + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // for explanations on the mapping modus, see 'loc_to_glob_mapper_particle.h' + switch (modus) { + case 0: { + DoFRef(mat, indexRow, indexCol) += lmat.value(fct1, dof1, fct2, dof2); + } + break; + case 1: { + DoFRef(mat, indexRow, transInd[fct2]) += lmat.value(fct1, dof1, fct2, dof2); + + for (size_t cmp = 0; cmp < dim; ++cmp) { + DoFRef(mat, indexRow, rotInd[cmp]) += lmat.value(fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp]; + } + + } + break; + case 2: { + if (!m_spCutElementHandler->get_DoF_modus_linear(prtIndex)) + DoFRef(mat, transInd[fct1], indexCol) += lmat.value( + fct1, dof1, fct2, dof2); + + // assemble entries, EVEN THOUGH it is: \int (r x p*n) = 0 + // --> fct2 == dim NECESSARY, since in case of irregular meshes, the entries can get inconsistent! + // --> this effect was tested and observed for some numerical test simulations + for (size_t cmp = 0; cmp < dim; ++cmp) { + if (transposed) { + if (!m_spCutElementHandler->get_DoF_modus_angular(prtIndex)) + DoFRef(mat, rotInd[cmp], indexCol) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatIP[cmp][fct1]; + } else { + if (1) //or: if ( fct2 != dim ) + { + if (!m_spCutElementHandler->get_DoF_modus_angular(prtIndex)) + DoFRef(mat, rotInd[cmp], indexCol) += + lmat.value(fct1, dof1, fct2,dof2) + * rotationMatIP[fct1][cmp]; + } + } + + } + + } + break; + case 3: { + + if (!m_spCutElementHandler->get_DoF_modus_linear(prtIndex)) + { + // trans -> trans + DoFRef(mat, transInd[fct1], transInd[fct2]) += + lmat.value(fct1, dof1, fct2, dof2); + + // trans -> rot: ( w x r = w x r_co = R_co^T*w); (R^T*... <=> [fct1][cmp]) + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, transInd[fct1], rotInd[cmp]) += + lmat.value(fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp]; + } + + if (!m_spCutElementHandler->get_DoF_modus_angular(prtIndex)) + { + if (bOld) { + if (transposed) { + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, rotInd[cmp], transInd[fct2]) += + lmat.value(fct1, dof1, fct2, + dof2) + * rotationMatIP[cmp][fct1]; + } else { + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(mat, rotInd[cmp], transInd[fct2]) += + lmat.value(fct1, dof1, fct2, + dof2) + * rotationMatIP[fct1][cmp]; + } + + // rot -> rot: ( r x (w x r) = r_ip x (w x r_co) = R_ip*R_co^T*w = (R_co*R_ip^T)^T*w) ; ) (...^T*w <=> [fct1][cmp]) + if (transposed) { + for (size_t cmp1 = 0; cmp1 < dim; ++cmp1) + for (size_t cmp2 = 0; cmp2 < dim; + ++cmp2) + DoFRef(mat, rotInd[cmp1], + rotInd[cmp2]) += lmat.value( + fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp2] + * rotationMatIP[cmp1][fct1]; + } else { + for (size_t cmp1 = 0; cmp1 < dim; ++cmp1) + for (size_t cmp2 = 0; cmp2 < dim; + ++cmp2) + DoFRef(mat, rotInd[cmp1], + rotInd[cmp2]) += lmat.value( + fct1, dof1, fct2, dof2) + * rotationMatCo[fct2][cmp2] + * rotationMatIP[fct1][cmp1]; + } + } else { + // use local couplings computed by class 'ParticleBndCond' + // and add those to global matrix + DoFRef(mat, rotInd[fct1], transInd[fct2]) += + get_rotJ_ind(fct1, dof1, fct2, dof2); + DoFRef(mat, rotInd[fct1], rotInd[fct2]) += + get_rotJ_rot(fct1, dof1, fct2, dof2); + } + } // end if (...DoF_modus_angular()) + + } + break; + case 4: { // to pressure outside: sum = e-20 => skip! + } + break; + + default: + throw(UGError("Error in LocToGlob!")); + + } // end switch-cases + + } // end dof2-loop + + //////////////////////////////////////////////////////////////////////////////////////////////// + // finally set dirichlet row for velocity DoFs on interface: + // ---> also done during 'set_identity_mat_constraint()', BUT only for OUTSIDE_DOM-elements!! + //////////////////////////////////////////////////////////////////////////////////////////////// + + bool bSetDirichletRowsForVelocity = + m_spInterfaceHandlerLocal->remapped_fromInterface(_dof1); + if (bSetDirichletRowsForVelocity) { + const size_t indexDiagInd = rowInd.index(0, _dof1); + if (indexDiagInd != transInd[0][0] + && indexDiagInd != rotInd[0][0]) { + for (size_t d = 0; d < dim; ++d) { + const DoFIndex indexDiag = DoFIndex( + rowInd.index(d, _dof1), rowInd.comp(d, _dof1)); + DoFRef(mat, indexDiag, indexDiag) = 1.0; + } + } else //if transInd or rotInd lie on cut element and are given by the user, i.e. NO DoFs! + { + // set dirichlet row for translation vel + if (m_spCutElementHandler->get_DoF_modus_linear( + prtIndex)) { + for (size_t d = 0; d < dim; ++d) { + const DoFIndex indexDiag = DoFIndex( + rowInd.index(d, _dof1), + rowInd.comp(d, _dof1)); + DoFRef(mat, indexDiag, indexDiag) = 1.0; + } + } + // set dirichlet row for rotation vel + if (m_spCutElementHandler->get_DoF_modus_angular( + prtIndex)) { + for (size_t d = 0; d < dim; ++d) { + const DoFIndex indexDiag = DoFIndex( + rowInd.index(d, _dof1), + rowInd.comp(d, _dof1)); + DoFRef(mat, indexDiag, indexDiag) = 1.0; + } + } + } + } + } // end dof1-loop + +} + +template +void ParticleMapper:: +assemble_QuadriCorners(vector_type& vec, + const LocalVector& lvec, std::vector transInd1, + std::vector rotInd1, std::vector transInd2, + std::vector rotInd2) +{ +// REMARK: not finally tested! + return; + + + if (dim == 3) + UG_THROW("in 'assemble_QuadriCorners()': not implemented!\n"); + + UG_LOG("transInd1: " << transInd1[0] << "transInd2: " << transInd2[0] << "\n"); + UG_LOG("rotInd1: " << rotInd1[0] << "rotInd2: " << rotInd2[0] << "\n"); + + bool remap = true; + + for (size_t fct = 0; fct < lvec.num_all_fct(); ++fct) { + for (size_t dof = 0; dof < lvec.num_all_dof(fct); ++dof) { + if (lvec.value(fct, dof) != lvec.value(fct, dof)) + UG_THROW("NAN in 'add_local_vec_to_global_FT()'!...\n"); + + size_t _dof = dof; + if (remap) + _dof = m_spInterfaceHandlerLocal->corner_orig(dof); + + + DoFRef(vec, transInd1[fct]) += lvec.value(fct, dof); + + MathVector RotVec; + RotVec[0] = -m_spInterfaceHandlerLocal->radial_at_ip(dof)[1]; + RotVec[1] = m_spInterfaceHandlerLocal->radial_at_ip(dof)[0]; + + DoFRef(vec, rotInd1[0]) += lvec.value(fct, dof) * RotVec[fct]; + + } // end dof-loop + } + + UG_THROW(" end assemble_QuadriCorners()\n\n"); + +} + +template +void ParticleMapper:: +add_local_vec_to_global_FT_for2_StdFV( + vector_type& vec, const LocalVector& lvec, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2) +{ +// REMARK: not finally tested! + UG_THROW("ParticleMapper::add_local_vec_to_global_FT_for2_StdFV(): not finally tested!!!!\n"); + + if (dim == 3) + UG_THROW("add_local_vec_to_global_FT_for2_StdFV() not implemented for 3d!!!\n"); + + bool remap = true; + + const LocalIndices& ind = lvec.get_indices(); + + for (size_t fct = 0; fct < lvec.num_all_fct(); ++fct) + for (size_t dof = 0; dof < lvec.num_all_dof(fct); ++dof) { + if (lvec.value(fct, dof) != lvec.value(fct, dof)) + UG_THROW("NAN in 'add_local_vec_to_global_FT()'!...\n"); + if (lvec.value(fct, dof) > 1e+10) + UG_THROW("-----------------------> 1e+10: " << lvec.value(fct,dof) << "\n"); + + size_t _dof = dof; + if (remap) + _dof = m_spInterfaceHandlerLocal->corner_orig(dof); + + const DoFIndex multi_index = DoFIndex(ind.index(fct, _dof),ind.comp(fct, _dof)); + const int prtIndex = getPrtIndex(_dof); + + bool isPrtNode = m_spInterfaceHandlerLocal->remapped_fromInterface(_dof); + + // usual assembling for fluid-dof and pressure-fct + if (!isPrtNode || fct == dim) { + DoFRef(vec, multi_index) += lvec.value(fct, dof); + UG_LOG("added for fct = " << fct << ", dof = " << dof << ": " << lvec.value(fct,dof) << "\n"); + } else { + MathVector RotVec; + RotVec[0] = -m_spInterfaceHandlerLocal->radial_at_ip(dof)[1]; + RotVec[1] = m_spInterfaceHandlerLocal->radial_at_ip(dof)[0]; + + DoFIndex transInd, rotInd; + + if (prtIndex == 0) { + transInd = transInd1[fct]; + rotInd = rotInd1[0]; + } else { + if (prtIndex != 1) + UG_THROW("prtIndex = " << prtIndex << "\n"); + + transInd = transInd2[fct]; + rotInd = rotInd2[0]; + } + // finally add connection between the 2 particles: + DoFRef(vec, transInd) += lvec.value(fct, dof); + DoFRef(vec, rotInd) += lvec.value(fct, dof) * RotVec[fct]; + } + + } +} + +template +void ParticleMapper:: +add_local_vec_to_global_FT_for2( + vector_type& vec, const LocalVector& lvec, + std::vector transInd1, std::vector rotInd1, + std::vector transInd2, std::vector rotInd2) +{ +// REMARK: not finally tested! + UG_THROW("ParticleMapper::add_local_vec_to_global_FT_for2(): not finally tested!!!!\n"); + + const LocalIndices& ind = lvec.get_indices(); + + for (size_t fct = 0; fct < lvec.num_all_fct(); ++fct) + for (size_t dof = 0; dof < lvec.num_all_dof(fct); ++dof) { + if (lvec.value(fct, dof) != lvec.value(fct, dof)) + UG_THROW("NAN in 'add_local_vec_to_global_FT()'!...\n"); + + const size_t dofMap = map_for2(dof); + const DoFIndex multi_index = DoFIndex(ind.index(fct, dofMap), + ind.comp(fct, dofMap)); + + // usual assembling for fluid-dof and pressure-fct + if (dof == 4 || fct == dim) { + DoFRef(vec, multi_index) += lvec.value(fct, dof); + } else { + + MathVector RotVec; + RotVec[0] = -m_spInterfaceHandlerLocal->radial_at_ip(dof)[1]; + RotVec[1] = m_spInterfaceHandlerLocal->radial_at_ip(dof)[0]; + + DoFIndex transInd, rotInd; + + if (dof < 2) { + transInd = transInd1[fct]; + rotInd = rotInd1[0]; + } else { + transInd = transInd2[fct]; + rotInd = rotInd2[0]; + } + // finally add connection between the 2 particles: + DoFRef(vec, transInd) += lvec.value(fct, dof); + DoFRef(vec, rotInd) += lvec.value(fct, dof) * RotVec[fct]; + + } + + } +} + +template +void ParticleMapper:: +add_local_vec_to_global_FT(vector_type& vec, const LocalVector& lvec, + std::vector transInd, std::vector rotInd) +{ + const LocalIndices& ind = lvec.get_indices(); + bool remap = true; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ATTENTION: lmat has the algebra-structure and size of m_locJ (i.e. bigger than standard, in case of QUADIR) +// BUT: the indices-infos are still the original one for the associated global indices: +// --> lmat.m_pRowIndex, lmat.m_pColIndex +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + LocalVector locMatOrig = lvec; + LocalVector locMatTrans = lvec; + LocalVector locMatRot = lvec; + locMatOrig = 0.0; + locMatTrans = 0.0; + locMatRot = 0.0; + + const int prtIndex = get_prtIndex(); + if (prtIndex == -1) + UG_THROW("ParticleMapper::add_local_mat_to_global_FT(): prtIndex invalid!: prtIndex = " + << prtIndex << "\n"); + + for (size_t fct = 0; fct < lvec.num_all_fct(); ++fct) { + for (size_t dof = 0; dof < lvec.num_all_dof(fct); ++dof) { + + // some security checks: + if (lvec.value(fct, dof) != lvec.value(fct, dof)) + UG_THROW("NAN in 'add_local_vec_to_global_FT()'!...\n"); + + if (lvec.value(fct, dof) > 1e+10) + UG_THROW("-----------------------> 1e+10: " << lvec.value(fct,dof) << "\n"); + + // if remapping is done, the solution space turns into the 'flat top' shape, since ALL + // DoFs on the interface get associated (mapped) onto the original vertex of the mesh + size_t _dof = dof; + if (remap) + _dof = m_spInterfaceHandlerLocal->corner_orig(dof); + + const DoFIndex multi_index = DoFIndex(ind.index(fct, _dof), + ind.comp(fct, _dof)); + + bool isPrtNode = m_spInterfaceHandlerLocal->remapped_fromInterface(_dof); + + // usual assembling for fluid-dof OR pressure-fct (continuity equation) + if (!isPrtNode || fct == dim) { + DoFRef(vec, multi_index) += lvec.value(fct, dof); + } + + if (!isPrtNode || fct == dim) { + // do nothing... + } else { + + // check, if the linear velocity is given by the user: => NO need to be assembled + if (!m_spCutElementHandler->get_DoF_modus_linear(prtIndex)) + DoFRef(vec, transInd[fct]) += lvec.value(fct, dof); + + // check, if the angular velocity is given by the user: => NO need to be assembled + if (!m_spCutElementHandler->get_DoF_modus_angular(prtIndex)) + { + //////////////////////////////////////////////////////////////////////////////////////////////////// + // for the entries of the angular velocity, they need to be multiplied by + // the matrix resutling from the cross product + MathVector RotVec; + + if (dim == 2) { + RotVec[0] = -m_spInterfaceHandlerLocal->radial_at_ip(dof)[1]; + RotVec[1] = m_spInterfaceHandlerLocal->radial_at_ip(dof)[0]; + } + MathMatrix rotationMatCo, rotationMatIP; + MathMatrix rotationMatIP_transposed; + if (dim == 3) { + rotationMatIP = m_spCutElementHandler->get_rotationMat( + m_spInterfaceHandlerLocal->radial_at_ip(dof)); + Transpose(rotationMatIP_transposed, rotationMatIP); + } + MathVector Pos; + Pos[0] = m_spInterfaceHandlerLocal->radial_at_ip(dof)[0]; + Pos[1] = m_spInterfaceHandlerLocal->radial_at_ip(dof)[1]; + + if (dim == 2) + DoFRef(vec, rotInd[0]) += lvec.value(fct, dof) * RotVec[fct]; + if (dim == 3) + for (size_t cmp = 0; cmp < dim; ++cmp) + DoFRef(vec, rotInd[cmp]) += lvec.value(fct, dof) + * rotationMatIP_transposed[cmp][fct]; + + } // end if (...DoF_modus_angular() ) + } + + } // end dof-loop + } + +} + + + +} // end namespace NavierStokes +} // end namespace ug + +#endif /* PARTICLE_MAPPER_IMPL_H_ */ diff --git a/incompressible/fv1/moving_particle/moving_particle.h b/incompressible/fv1/moving_particle/moving_particle.h new file mode 100644 index 0000000..b88c5e8 --- /dev/null +++ b/incompressible/fv1/moving_particle/moving_particle.h @@ -0,0 +1,597 @@ +/* + * moving_particle.h + * + * Created on: 20.01.2015 + * Author: suze + */ + +#ifndef MOVING_PARTICLE_H_ +#define MOVING_PARTICLE_H_ + +#ifdef UG_PARALLEL + #include "lib_grid/parallelization/load_balancer_util.h" + #include "../../../../Parmetis/src/unificator_interface.h" + #include "lib_grid/grid/neighborhood.h" + #include "lib_grid/grid/neighborhood_util.h" // for GetConnectedNeighbor + #include "lib_grid/grid/grid_util.h" + #include "lib_grid/grid/grid.h" + #include "lib_grid/grid/grid_base_object_traits.h" // for geometry_traits +#endif + +#include "../../incompressible_navier_stokes_base.h" + +#include "lib_disc/spatial_disc/immersed_util/interface_handler/interface_handler_flat_top_cut/interface_handler_particle.h" +#include "loc_to_glob_mapper_particle.h" +#include "immersed_bnd_cond_particle.h" + +// for method 'mark_collision_area()': +#include "lib_grid/refinement/adaptive_regular_mg_refiner.h" + +#include +#include + + +namespace ug{ +namespace NavierStokes{ + + + +template < typename TDomain, typename TAlgebra> +class MovingParticle + : public IImmersedInterface +{ + public: + /// world Dimension + static const int dim = TDomain::dim; + + /// Algebra type + typedef TAlgebra algebra_type; + + /// Type of algebra matrix + typedef typename algebra_type::matrix_type matrix_type; + + /// Type of algebra vector + typedef typename algebra_type::vector_type vector_type; + + typedef typename domain_traits::grid_base_object grid_base_object; + + MovingParticle(SmartPtr > ass, + SmartPtr > spMaster, + SmartPtr > cutElementHandler, + number fluidDensity, number fluidKinVisc); + + // destructor + virtual ~MovingParticle(){}; + + ////////////////////////////////////////////////////////////////////////////////////////// + // main methods + ////////////////////////////////////////////////////////////////////////////////////////// + + // general initialisation of set up data; + // most important: call of 'update_interface_data()' during init() of CutElementHandler + // --> marks the cut elements and interface vertices as + // INSIDE, OUTSIDE, CUT_BY_INTERFACE(element)/ON_INTERFACE(vertx) + // --> sets up the element lists CutElementHandlerParticle::m_vvvElemList, + // ::m_vvvElemListCut, ::m_vvvElemListOutside + /// called via .lua: + void init(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, + const int topLevel); + + // mainly updates the coordinates of the particles for the next time step and updates the marker accordingly + // --> for that the solution in the DoFs for the particle motion needs to be + // (1) copied from DoFRef(u, ) into external storage: via call of 'store_particle_velocity()' + // (2) written back from storage to DoFRef(u, ): via call of 'write_particle_velocity()' + void update(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, + const int topLevel, const number time, const number deltaT); + +#ifdef UG_PARALLEL + void pre_balancing_update(vector_type& u, SmartPtr > spApproxSpace, + const int baseLevel, const int topLevel, const number time, number deltaT); + + void post_balancing_update(vector_type& u, SmartPtr > spApproxSpace, + const int baseLevel, const int topLevel, const number time, number deltaT); +#endif + + ////////////////////////////////////////////////////////////////////////////////////////// + // helper methods for init() and update() + ////////////////////////////////////////////////////////////////////////////////////////// + + // transfer direction: + // DoFRef(u, transInd) ---> ParticleProvider::m_vvLinearVelocity/m_vvAngularVelocity + void store_particle_velocity(vector_type& u, const int topLevel); + + // transfer direction: + // ParticleProvider::m_vvLinearVelocity/m_vvAngularVelocity ---> DoFRef(u, transInd) + void write_particle_velocity(vector_type& u, const int topLevel); + + ////////////////////////////////////////////////////////////////////////////////////////// + // write solution to 'FREED' nodes, i.e. nodes which were inside particle in the last + // time step and are outside the particle (i.e. freed) in the current time step + // --> call method via .lua BEFORE 'solTimeSeries:push_discard_oldest(oldestSol, time)': + // (if not: in case that outside nodes are inside the domain AFTER updating the + // particle coordinates, NO solution will be defined here!) + void fill_freed_nodes(vector_type& u, const int topLevel, const number time); + + ////////////////////////////////////////////////////////////////////////////////////////// + // helper methods for init() and update() + ////////////////////////////////////////////////////////////////////////////////////////// + + // currently not used + void compute_gradient_local_max(vector_type& sol, vector_type& grad, + SmartPtr > spApproxSpace, const int topLevel); + + /// writes the physical velocity values to the nodes lying in the particle domain + // (call via lua for visualisation purposes) + void adjust_global_solution(vector_type& u, const int topLevel); + + + ////////////////////////////////////////////////////////////////////////////////////////// + /// methods for setting up the forces for collision (by Jonas Simon, see Master Thesis) + ////////////////////////////////////////////////////////////////////////////////////////// + + void set_repulsive_force(bool repForce, number forceValue) + { m_spInterfaceMapper->set_repulsive_force(repForce, forceValue); } + void set_glowinski_repulsive_force(bool maxRepForce, number rho, number eps) + { m_spInterfaceMapper->set_glowinski_repulsive_force(maxRepForce, rho, eps); } + void set_minimum_correction_force(bool EquiRepForce, number repulsiveDistance) + { UG_THROW("Function is deprecated. Please use estimate_repulsive_force_parameters to calculate and set this force."); } + + bool mark_collision_area(SmartPtr refiner, int level); + bool estimate_repulsive_force_parameters(vector_type& u, int topLevel, double MaxElemDiameter, double deltaT); + void set_forceLog(bool val) { m_spInterfaceMapper->set_forceLog(val); } + void set_mpi_routine(int val){ m_spCutElementHandler->set_mpi_routine(val); } + + + ////////////////////////////////////////////////////////////////////////////////////////// + // some getter and setter methods + ////////////////////////////////////////////////////////////////////////////////////////// + + SmartPtr > get_BndCond() { return m_spInterfaceBndCond; } + + /// checks if grid data is updated and returns the 'levIndex' of the 'gridLevel' via access to 'CutElementHandlerBase::m_Map' + int get_Index(const GridLevel& gridLevel); + + /// returns true, if computation is time dependent + bool is_time_dependent() { return m_spInterfaceMapper->is_time_dependent();} + + + ////////////////////////////////////////////////////////////////////////////////////////// + /// lua-methods for set up: + ////////////////////////////////////////////////////////////////////////////////////////// + + // the 'threshold' defines the bandwidth around the immersed interface, in which a node + // counts as 'OUTSIDE' or 'ON_INTERFACE' during call of 'CutElementHandler::is_outside()' + // and 'CutElementHandler::is_nearInterface() + void initialize_threshold(TDomain& domain, const int baseLevel, const int topLevel); + void set_threshold(size_t level, const number threshold) + { m_spCutElementHandler->set_threshold(level, threshold); } + number MeanElementDiameter(TDomain& domain, int level); + + // If StdFV-assembling is ON, NO new 'vCornerCoords' will be computed on the cut elements. + // The original nodes ACCROSS the interface (ON the euclidian mesh) will be chosen for the + // computation of the solution of the interface + // ==> the standard shape functions w.r.t. the non-conforming mesh will be used + // (similar as in common 'ficticious domain' methods) + // ==> the shape functions will NOT be 1 ON the interface and the gradient will NOT + // point normal to the interface + void set_StdFV_assembling(bool bValue) { m_spInterfaceHandlerLocal->set_StdFV_assembling(bValue);} + bool StdFV_assembling() { return m_spInterfaceHandlerLocal->StdFV_assembling(); } + + // setting and getting flag for printing of cut-element data into file: + void set_print_cutElemData(bool bValue) { m_spInterfaceHandlerLocal->set_print_cutElemData(bValue); } + + void set_element_diameter(double val) { m_spInterfaceMapper->set_element_diameter(val); } + void set_volume_comp_mode(bool bVolumeCompMode) { m_spInterfaceMapper->set_volume_comp_mode(bVolumeCompMode); } + void set_gravity(bool gravity, number gravityConst) { m_spInterfaceMapper->set_gravity(gravity, gravityConst); } + void set_time_step(number dt) { m_spInterfaceMapper->set_time_step(dt); } + + // writes the velocity of the prtIndex-th particle into 'transSol' and 'rotSol' for further computatino or output + void get_velocity(MathVector& transSol, MathVector& rotSol, const vector_type& u, + const int topLevel, number deltaT, const size_t prtIndex); + + + ////////////////////////////////////////////////////////////////////////////////////////// + // lua-methods for output + ////////////////////////////////////////////////////////////////////////////////////////// + + /// writing data to file; called via .lua + void print_velocity(const vector_type& u, const int topLevel, number time, const char* filename); + + // writes the pressure on the front and back of the cylinder and the delta of it into a file + void print_deltaP(const vector_type& u, const int topLevel); + + // writes the pressure value along the circular interface in grid nodes into file + void print_pressure_nodal(const vector_type& u, const int topLevel); + + // writes the pressure value along the circular interface for equidistant angle teta into file + void print_pressure_teta(const vector_type& u, const int topLevel); + + + // interpolates the value at a non-grid point (used for the computation of deltaP in 'print_deltaP()' + void interpolate_point(ConstSmartPtr dd, const vector_type& u, + const MathVector& evalPos, MathVector& interpolation); + + // returns the number of DoFs on the original grid + size_t get_numCutElements(const int gridlevel, const size_t prtIndex) + { + ConstSmartPtr dd = m_spApproxSpace->dof_distribution(GridLevel(gridlevel, GridLevel::LEVEL)); + const int levIndex = m_spCutElementHandler->get_Index(gridlevel, dd); + + return m_spCutElementHandler->get_numCutElements(levIndex, prtIndex); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // class member + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private: + // new member + SmartPtr > m_spCutElementHandler; + + // member from base class + SmartPtr > m_spInterfaceHandlerLocal; // contains class member + // 'CutElementHandler_FlatTop' + SmartPtr > m_spInterfaceMapper; // contains class member + // 'IInterfaceHandlerLocal' + SmartPtr > m_spInterfaceBndCond; // contains class member + // 'IInterfaceHandlerLocal' + + /// current ApproxSpace + SmartPtr > m_spApproxSpace; + + +}; // end class MovingParticle + + + + +#ifdef UG_PARALLEL +template +class ParticleUnificator : public parmetis::IUnificator::base_obj_type> +{ + + public: + typedef typename GeomObjBaseTypeByDim::base_obj_type elem_t; + typedef typename elem_t::side side_t; + typedef Attachment AElemIndex; + typedef Attachment > AElemIndices; + + /// world Dimension + static const int dim = TDomain::dim; + + /// Type of position coordinates + typedef MathVector position_type; + + /// Type of Position Attachment + typedef Attachment position_attachment_type; + + /// Type of Accessor to the Position Data Attachment + typedef Grid::VertexAttachmentAccessor + position_accessor_type; + + /// constructor + ParticleUnificator(SmartPtr spDom) + : m_spMG(spDom->grid()) + { + // get position attachment + m_aPos = GetDefaultPositionAttachment(); + + // let position accessor access Vertex Coordinates + if(!m_spMG->has_attachment(m_aPos)) + m_spMG->attach_to(m_aPos); + m_aaPos.access(*m_spMG, m_aPos); + } + + void update_particles(SmartPtr > provider ) + { + m_vParticleCoord.clear(); + + size_t numPrt = provider->num_particles(); + for ( size_t i = 0; i < numPrt; ++i ) + m_vParticleCoord.push_back(std::make_pair(provider->get_radius(i), provider->get_center(i))); + + UG_LOG("in update_particles:\n") + for ( size_t i = 0; i < m_vParticleCoord.size(); ++i ) + UG_LOG("m_vParticleCoord: " << m_vParticleCoord[i].second << "\n"); + + UG_LOG("in update_particles end....\n\n\n"); + + } + + virtual void unify( + MultiGrid* mg, + int lvl, + int localOffset, + const Grid::AttachmentAccessor& aaElemInd, // local indices! + const Grid::AttachmentAccessor& aaSideElemInd, // global indices! + std::vector >& unificationPairs) const // global indices! + { + const DistributedGridManager& dgm = *mg->distributed_grid_manager(); + + std::vector vNeighbors; + + //UG_LOG("unify() on lvl " << lvl << "\n"); + + typename geometry_traits::iterator iterEnd, iter; + iter = mg->begin(lvl); + iterEnd = mg->end(lvl); + + // iterate all elements + for(; iter != iterEnd; ++iter) { + elem_t* elem = *iter; + + int locInd = aaElemInd[elem]; + + //UG_LOG("element is " << locInd << "..\n"); + + // skip ghosts + if (locInd == -1) { + continue; + } + + // only process particle elements. + if (is_PartOfParticle(elem)) { + //UG_LOG("local index is " << locInd << " and global index is " << locInd + localOffset << "\n"); + + //std::vector sides; + typename Grid::traits::secure_container sides; + //CollectAssociated(sides, *mg, elem, true); + mg->associated_elements(sides, elem); + //UG_LOG("sides has length " << sides.size() << "\n"); + // iterate over current element's sides + for (size_t i = 0; i < sides.size(); ++i) { + //UG_LOG("Check side between elements " << aaSideElemInd[sides[i]][0] << " and " << aaSideElemInd[sides[i]][1] << ".\n"); + // check if neighbor on the other side is stored on another process + // if not use aaElemInd with local indices + if (!dgm.is_in_horizontal_interface(sides[i])) // QUESTION: Check elem_t or side_t here? + { + // get neighbors of element + //CollectNeighbors(vNeighbors, elem, *mg); + + // iterate over neighbor elements + /*for (elem_t* neighbor : vNeighbors) { + + // check if neighbor element is inside particle too + if (is_PartOfParticle(neighbor)) { + UG_LOG("Add " << aaElemInd[elem]+localOffset << "(" << localOffset << ") and " << aaElemInd[neighbor]+localOffset << " to unificationPairs\n"); + unificationPairs.push_back(std::make_pair(aaElemInd[elem] + localOffset, aaElemInd[neighbor] + localOffset)); + } + }*/ + + // get back the elements to current side and add them to unificationPairs + typename Grid::traits::secure_container elemList; + mg->associated_elements(elemList, sides[i]); + UG_COND_THROW(elemList.size() > 2, "More than two " << elemList[0]->reference_object_id() + << "s associated with " << sides[i]->reference_object_id() << "."); + + if (elemList.size() == 2) + { + int locInd0 = aaElemInd[elemList[0]]; + int locInd1 = aaElemInd[elemList[1]]; + + // exclude ghosts + if (locInd0 == -1 || locInd1 == -1) + continue; + + // add to unification pairs + //UG_LOG("(1) Add " << locInd0+localOffset << "(" << localOffset << ") and " << locInd1+localOffset << " to unificationPairs\n"); + unificationPairs.push_back(std::make_pair(locInd0+localOffset, locInd1+localOffset)); + } else { + //UG_LOG("elemList.size() = " << elemList.size() << " != 2 shouldn't appear.\n"); + } + } + // if other processes have to be addressed for neighbors, use aaSideElemInd with global indices instead + else{ + //UG_LOG("Elements on different processes\n"); + const std::vector& inds = aaSideElemInd[sides[i]]; + + // if element side is part of global boundary aaSideElemInd[elem] holds only the element itself. + if (inds.size() == 1) + continue; + + UG_ASSERT(inds.size() == 2, "Indices attachment vector has more or less than 2 entries."); + //UG_LOG("(2) Add " << inds[0] << " and " << inds[1] << " to unificationPairs\n"); + unificationPairs.push_back(std::make_pair(inds[0], inds[1])); + } + } + } + } + //UG_LOG("unify() finished.\n"); + } + + /** + * Function that estimates if any particle is close to any process border. + */ + bool rebalance(MultiGrid* mg, int lvl){ + typename geometry_traits::iterator iterEnd, iter; + iter = mg->begin(lvl); + iterEnd = mg->end(lvl); + + int repart = 0; + + // iterate over all elements + for(; iter != iterEnd; ++iter) { + elem_t* elem = *iter; + typename Grid::traits::secure_container sides; + mg->associated_elements(sides, elem); + + // iterate over current element's sides + for (size_t i = 0; i < sides.size(); ++i) { + // get back the elements to current side and add them to unificationPairs + typename Grid::traits::secure_container elemList; + mg->associated_elements(elemList, sides[i]); + // if border element + // UG_LOG("elemList.size(): " << elemList.size()) + if (elemList.size() < 2) { + if (is_PartOfParticle(elemList[0])) { + repart = 1; + } + } + } + } + + pcl::ProcessCommunicator com; + int repart_all = 0; + com.allreduce(&repart, &repart_all, 1, MPI_INT, PCL_RO_SUM); + + return repart_all; + } + + private: + bool is_PartOfParticle(elem_t* elem) const + { + bool part = false; + for (size_t p = 0; p < m_vParticleCoord.size(); ++p) { + // special case if particle is smaller than one element of the multigrid's base level -> ist das überhaupt relevant? kann ein Baselevelelement partitioniert werden? + if (ContainsPoint(elem, m_vParticleCoord[p].second, m_aaPos)) { + //UG_LOG("Particle center is inside this element.\n"); + return true; + } + // check if at least one vertex is inside particle + for(size_t i = 0; i < elem->num_vertices(); ++i) { + if (is_insideParticle(elem->vertex(i),p)) { + return true; + } + } + // // exclude triangle-circle overlap + // for(size_t i = 0; i < elem->num_vertices(); ++i) { + // for(size_t j = i; j < elem->num_vertices(); ++j) { + // if (intersection(m_vParticleCoord[p].second,m_vParticleCoord[p].first,m_aaPos[elem->vertex(i)],m_aaPos[elem->vertex(j)])) { + // return true; + // } + // } + // } + + MathVector elemCenter = m_aaPos[elem->vertex(0)]; + for(size_t i = 1; i < elem->num_vertices(); ++i) { + // const MathVector& vrtPos = m_aaPos[elem->vertex(i)]; + // for (size_t j = 0; j < dim; ++j) { + // elemCenter[j] += vrtPos[j]; + // } + elemCenter += m_aaPos[elem->vertex(i)]; + } + elemCenter /= elem->num_vertices(); + //UG_LOG("Element Center is (" << elemCenter[0] << ", " <num_vertices(); ++i) { + number distance = VecDistance(m_aaPos[elem->vertex(i)],elemCenter); + maxDistance = (maxDistance > distance) ? maxDistance : distance; + } + //UG_LOG("maxDistance is " << maxDistance << ".\n"); + const number radius = m_vParticleCoord[p].first; + const MathVector& center = m_vParticleCoord[p].second; + number centerDistance = VecDistance(elemCenter, center); + //UG_LOG("centerDistance is " << centerDistance << ".\n"); + part = (part or (radius + maxDistance > centerDistance)); + } + return part; + //return false; + } + + bool is_insideParticle(Vertex* vrt, size_t prtIndex) const + { + // get distance to first radius as initial value + const MathVector& vrtPos = m_aaPos[vrt]; + const MathVector& center = m_vParticleCoord[prtIndex].second; + const number radius = m_vParticleCoord[prtIndex].first; + + if (VecDistance(vrtPos, center) <= radius) { + return true; + } + return false; //set_nearInterface(vrt, prtIndex); + } + + bool is_outside(Vertex* vrt) + { + // loop over all centers and pick the index with minimal distance + for (size_t p = 0; p < m_vParticleCoord.size(); ++p) + if ( !is_insideParticle(vrt, p) ) + return true; + return false; + } + + bool conn_is_cut_by_interface(side_t* conn) + { + // init data + bool insideFluid = false; + bool outsideFluid = false; + + // loop vertices + for(size_t i = 0; i < conn->num_vertices(); ++i) + { + // check if inside a particle + if (is_outside(conn->vertex(i))) outsideFluid = true; + else insideFluid = true; + + //if (insideFluid && is_nearInterface(vrt)) + // UG_THROW("in 'get_elem_modus()': case 'set_nearInterface(vrt) = true' not possible!\n"); + + } // vertex loop + + // return elem type + if (insideFluid && outsideFluid) { + return true; + //UG_LOG("edge " << conn->grid_data_index() << " should have weight " << m_weight); + } + return false; + + } + + bool intersection(MathVector center, double radius, MathVector vertex1, MathVector vertex2) const { + number alpha; + + MathVector lineDir; + MathVector rayDir; + // lineDir = vertex1 - vertex2; + VecSubtract(lineDir, vertex1, vertex2); + // rayDir = vertex2 - center; + VecSubtract(rayDir, vertex2, center); + + const number a = VecDot(lineDir, lineDir); + const number b = 2.0 * VecDot(lineDir, rayDir); + const number c = VecDot(vertex2, vertex2) + VecDot(center, center) + - 2 * VecDot(vertex2, center) - radius * radius; + + const number discriminant = b * b - 4 * a * c; + + // check that 'vrtPosOut' and 'vrtPosIn' really lie on different sides of the circle: + //if (discriminant < -1e-8) + // UG_LOG("discriminant is " << discriminant << " < -1e-8\n"); + //UG_THROW("Value of discriminant = " << discriminant << "\n"); + + // discriminant = 0! + //const number alpha1 = (-b - sqrt(discriminant)) / (2.0 * a); + //const number alpha2 = (-b + sqrt(discriminant)) / (2.0 * a); + + // if (alpha1 <= alpha2) + // alpha = alpha1; + // else + // alpha = alpha2; + + if (discriminant < 0) + return false; + else + return true; + } + + SmartPtr m_spMG; + position_attachment_type m_aPos; /// > > m_vParticleCoord; + +}; // end class ParticleUnificator + +#endif + +} // end namespace MovingParticle +} // end namespace ug + + +#include "moving_particle_impl.h" +#include "moving_particle_tools.h" + + + +#endif /* MOVING_PARTICLE_H_ */ diff --git a/incompressible/fv1/moving_particle/moving_particle_impl.h b/incompressible/fv1/moving_particle/moving_particle_impl.h new file mode 100644 index 0000000..df2a928 --- /dev/null +++ b/incompressible/fv1/moving_particle/moving_particle_impl.h @@ -0,0 +1,963 @@ +/* + * moving_particle_impl.h + * + * Created on: 20.01.2015 + * Author: suze + */ + +#ifndef MOVING_PARTICLE_IMPL_H_ +#define MOVING_PARTICLE_IMPL_H_ + +#include + +namespace ug { +namespace NavierStokes { + + +/////////////////////////////////////////////////////////// +// Implementation of the methods class +// 'MovingParticle' +/////////////////////////////////////////////////////////// + +template +MovingParticle::MovingParticle( + SmartPtr > ass, + SmartPtr > spMaster, + SmartPtr > cutElementHandler, + number fluidDensity, number fluidKinVisc) +: m_spCutElementHandler(cutElementHandler), + m_spInterfaceHandlerLocal( + new InterfaceHandlerLocalParticle(cutElementHandler, fluidDensity, fluidKinVisc) ), + m_spInterfaceMapper( + new ParticleMapper(m_spInterfaceHandlerLocal)), + m_spInterfaceBndCond( + new ParticleBndCond(spMaster, m_spInterfaceHandlerLocal)) +{ + + if (cutElementHandler->num_particles() == 0) + UG_THROW("MovingParticle::Constructor(): no particles initializen in 'cutElementHandler\n"); + +// initialize singleton and set local handler + typedef DimFV1FTGeometry > TFVGeom; + TFVGeom& geo = GeomProvider::get(LFEID(LFEID::LAGRANGE, dim, 1), 1); + geo.set_interface_handler(m_spInterfaceHandlerLocal); + +// initialize mapper within domainDisc: + SmartPtr > assAdapt = ass->ass_tuner(); + assAdapt->set_mapping(m_spInterfaceMapper.get()); + +// needs to be enabled, in order to call 'spAssTuner->modify_LocalData()' during element disc assembling + assAdapt->enable_modify_solution(true); + +} + + +template +void MovingParticle:: +init(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, + const int topLevel) +{ + // get data + m_spApproxSpace = spApproxSpace; + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + +// call of 'CutElementHadlerFT::update_interface_data(): for all given levels + m_spCutElementHandler->template init(dd, baseLevel, topLevel); + +// transfer particle velocity: +// FROM m_vvLinearVelocity/m_vvAngularVelocity (initialized during 'ParticleProvider::add()' +// TO DoFRef(u, transInd)/DoFRef(u, rotInd) (for the later solution process) + write_particle_velocity(u, topLevel); + +} + + +template +void MovingParticle:: +update(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, + const int topLevel, const number time, const number deltaT) +{ + int topLev = spApproxSpace->num_levels()-1; + if ( topLev != topLevel ) + UG_THROW("NavierStokes::update: parameter 'topLevel' = " << topLevel << " != " + << topLev << "current top leven! \n"); + + // A. copy particle velocity to data: DoFRef(u, transInd/rotInd) ---> m_vvLinearVelocity/m_vvAngularVelocity + // reverse direction done during 'NavierStokes::write_particle_velocity()' --> see F. + store_particle_velocity(u, topLevel); + +#ifdef UG_PARALLEL + // B. synchronize particle data when everything in ParticleProvider is up to date. + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + m_spCutElementHandler->synchronize_particles(levIndex); +#endif + + // C. calculate new particle coordinates + m_spCutElementHandler->update_prtCoords(topLevel, deltaT); + + // D. write solution to 'FREED' nodes, i.e. nodes which were inside particle in the last + // time step and are outside the particle (i.e. freed) in the current time step + // --> needs to be called AFTER 'update_prtCoords(), since being inside particle refers + // already to new coordinates + // --> needs to be called BEFORE 'm_spCutElementHandler->template init(), since being + // inside particle refers to OLD BoolMarker + fill_freed_nodes(u, topLevel, time); + + // E. update marker and global indices => new location for (u, transInd), (u, rotInd) ! + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + m_spCutElementHandler->template init(dd, baseLevel, topLevel); + + // F. write solution into new location of global indices 'transInd' and 'rotInd': + // transfer direction: m_vvLinearVelocity/m_vvAngularVelocity ---> DoFRef(u, transInd)/DoFRef(u, rotInd) + write_particle_velocity(u, topLevel); + + // reset volume: + m_spInterfaceMapper->reset_volume(); +} + +template +int MovingParticle:: +get_Index(const GridLevel& gridLevel) +{ + ConstSmartPtr dd = m_spApproxSpace->dof_distribution(gridLevel); + + const int levIndex = m_spCutElementHandler->get_Index(gridLevel, dd); + + return levIndex; +} + +template +number MovingParticle:: +MeanElementDiameter(TDomain& domain, int level) +{ + + typedef typename domain_traits::grid_base_object TElem; + typedef typename geometry_traits::iterator ListIter; + + ListIter iter = domain.grid()->template begin(level); + ListIter iterEnd = domain.grid()->template end(level); + + number mean = 0.0; + size_t numIter = 0; + for (; iter != iterEnd; ++iter) { + mean += ElementDiameterSq(*domain.grid(), domain.position_accessor(), + *iter); + numIter++; + } + + mean = mean / numIter; + +#ifdef UG_PARALLEL + // share value between all procs + pcl::ProcessCommunicator com; + // ToDO: PCL_RO_MIN oder MAX oder doch mean noch berechnen m.H.v. /numProcs??? + //UG_THROW("in MeanElementDiameter(): was macht 'allredue' im Parallelen???\n"); + mean = com.allreduce(mean, PCL_RO_MIN); +#endif + + UG_LOG("nach com.allreduce: mean = " << std::sqrt(mean) << "\n"); + return std::sqrt(mean); +} + +template +void MovingParticle:: +initialize_threshold(TDomain& domain, const int baseLevel, const int topLevel) +{ + if (baseLevel < 0) + UG_THROW("initialize_threshold(): no cast of baselevel from 'int' tp 'size_t' possible! \n"); + if (topLevel < 0) + UG_THROW("initialize_threshold(): no cast of toplevel from 'int' tp 'size_t' possible! \n"); + + typedef typename domain_traits::grid_base_object TElem; + +// compute level-dependent value for threshold: + for (size_t lev = baseLevel; lev <= (size_t) topLevel; ++lev) { + const number maxLength = MaxElementDiameter(domain, lev); + const number meanLength = MeanElementDiameter(domain, lev); + UG_LOG("maxLength = " << maxLength << "\n"); + UG_LOG("meanLength = " << meanLength << "\n"); + UG_LOG("threshold_max = " << maxLength*maxLength << "\n"); + UG_LOG("threshold_mean = " << meanLength*meanLength << "\n"); + + set_threshold(lev, meanLength * meanLength); + + } + + UG_LOG("----------------- END initialize_threshold() ---------------- \n"); + +} + + +// transfer direction: DoFRef(u, transInd) ---> m_vvLinearVelocity/m_vvAngularVelocity +template +void MovingParticle:: +store_particle_velocity(vector_type& u, const int topLevel) +{ + bool output = false; + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) { + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order + // to indicate, whether a particle lies on a processor or not + std::vector ElemList = m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 MovingParticle::store_particle_velocity: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 MovingParticle::store_particle_velocity: ElemList.size(): " + << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + if ( output ){ + UG_LOG("copy_solution(): VORHER transSol(0) = " << m_spCutElementHandler->get_transSol(p,0) << "\n"); + UG_LOG("copy_solution(): VORHER transSol(1) = " << m_spCutElementHandler->get_transSol(p, 1) << "\n"); + UG_LOG("copy_solution(): VORHER rotSol(0) = " << m_spCutElementHandler->get_rotSol(p,0) << "\n"); + UG_LOG("copy_solution(): VORHER rot1Sol(1) = " << m_spCutElementHandler->get_rotSol(p, 1) << "\n"); + } + + // 'get_transInd()' returns NOT YET updated indices (updated during 'movingParticle:update()' vie lua) + std::vector < DoFIndex > transInd = + m_spCutElementHandler->get_transInd(levIndex, p); + std::vector < DoFIndex > rotInd = + m_spCutElementHandler->get_rotInd(levIndex, p); + + UG_LOG(" transInd(0) = " << transInd[0] << "\n"); + UG_LOG(" rotInd(0) = " << rotInd[0] << "\n"); + + // finally: write solution from m_vvLinearVelocity/m_vvAngularVelocity to DoFRef and set + // solution in DoFRef to zero + for (int d = 0; d < dim; ++d) + { + number solution = DoFRef(u, transInd[d]); + m_spCutElementHandler->set_extraSolTrans(solution, p, 0, d); + DoFRef(u, transInd[d]) = 0.0; + + solution = DoFRef(u, rotInd[d]); + m_spCutElementHandler->set_extraSolRot(solution, p, 0, d); + DoFRef(u, rotInd[d]) = 0.0; + } + + if ( output ){ + UG_LOG("copy_solution(): NACHHER transSol(0) = " << m_spCutElementHandler->get_transSol(p,0) << "\n"); + UG_LOG("copy_solution(): NACHHER transSol(1) = " << m_spCutElementHandler->get_transSol(p, 1) << "\n"); + UG_LOG("copy_solution(): NACHHER rotSol(0) = " << m_spCutElementHandler->get_rotSol(p,0) << "\n"); + UG_LOG("copy_solution(): NACHHER rot1Sol(1) = " << m_spCutElementHandler->get_rotSol(p, 1) << "\n"); + } + + } // end particle loop + +} + + + +template +void MovingParticle:: +get_velocity(MathVector& transSol, MathVector& rotSol, const vector_type& u, + const int topLevel, number deltaT, const size_t prtIndex) +{ + // get data + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order + // to indicate, whether a particle lies on a processor or not + std::vector ElemList = m_spCutElementHandler->m_vvvElemListCut[levIndex][prtIndex]; + UG_LOG("1 MovingParticle::get_velocity: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 MovingParticle::get_velocity: ElemList.size(): " << ElemList.size() << + " => skip assembling! \n"); + return; + } +#endif + // get multiindices for translation and rotation of particle + std::vector < DoFIndex > transInd = m_spCutElementHandler->get_transInd(levIndex, prtIndex); + std::vector < DoFIndex > rotInd = m_spCutElementHandler->get_rotInd(levIndex, prtIndex); + + + for ( int d = 0; d < dim; ++d ) + { + transSol[d] = DoFRef(u, transInd[d]); + rotSol[d] = DoFRef(u, rotInd[d]); + } + +} + +template +void MovingParticle:: +compute_gradient_local_max(vector_type& sol, vector_type& grad, + SmartPtr > spApproxSpace, const int topLevel) +{ + +// get data + typedef typename domain_traits::grid_base_object grid_base_object; + typename DoFDistribution::traits::const_iterator iter, iterEnd; + + ConstSmartPtr dd = spApproxSpace->dof_distribution( + GridLevel(topLevel, GridLevel::LEVEL)); + typename TDomain::position_accessor_type aaPos = + m_spCutElementHandler->m_aaPos; + + iter = dd->template begin(); + iterEnd = dd->template end(); + +// loop elements in order to compute the volume and set rhs: + for (; iter != iterEnd; iter++) { + // get element + grid_base_object* elem = *iter; + + if (m_spCutElementHandler->m_spOutsideMarker->is_marked(elem)) + continue; + + std::vector < DoFIndex > vInd1; + std::vector < DoFIndex > vInd2; + + std::vector vEdges; + CollectEdgesSorted(vEdges, *m_spCutElementHandler->m_spMG, elem); + + if (m_spCutElementHandler->m_spCutMarker->is_marked(elem)) { + for (size_t e = 0; e < vEdges.size(); ++e) { + Edge* edge = vEdges[e]; + std::vector vVertexEdge; + CollectVertices(vVertexEdge, *m_spCutElementHandler->m_spMG, + edge); + if (vVertexEdge.size() != 2) + UG_THROW("error in collecting vertices associated to an edge!...EXIT!...\n"); + + Vertex* vrt1 = vVertexEdge[0]; + Vertex* vrt2 = vVertexEdge[1]; + + // no gradient needs to be computed on outside edges: + if (m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt1) + && m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt2)) + continue; + + if (dd->inner_dof_indices(vrt1, dim, vInd1) != 1) + UG_THROW("MovingParticle::compute_gradient(): Only one index expected."); + if (dd->inner_dof_indices(vrt2, dim, vInd2) != 1) + UG_THROW("MovingParticle::compute_gradient(): Only one index expected."); + + // compute local gradient along the edge + number grad_edge = fabs(DoFRef(sol, vInd1[0]) - DoFRef(sol, vInd2[0])); + number edgeLength = VecDistance(aaPos[vrt1], aaPos[vrt2]); + + if (!m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt1) + && !m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt2)) { // nothing has to be adapted! + } else if (m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt1)) { + if (m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt2)) + UG_THROW("hmmm...wrong in 'compute_gradient()'...\n"); + + // compute intersectionPoint: + MathVector intersectionPnt; + if (m_spInterfaceHandlerLocal->is_nearInterfaceVertex(vrt1)) + VecCopy(intersectionPnt, aaPos[vrt1], 0.0); + else + m_spInterfaceHandlerLocal->get_intersection_point( + intersectionPnt, vrt2, vrt1); + + // compute edgeLength: + edgeLength = VecDistance(aaPos[vrt2], intersectionPnt); + } else if (m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt2)) { + if (m_spInterfaceHandlerLocal->is_onInterfaceVertex(vrt1)) + UG_THROW("hmmm...wrong in 'compute_gradient()'...\n"); + + // compute intersectionPoint: + MathVector intersectionPnt; + if (m_spInterfaceHandlerLocal->is_nearInterfaceVertex(vrt2)) + VecCopy(intersectionPnt, aaPos[vrt2], 0.0); + else + m_spInterfaceHandlerLocal->get_intersection_point( + intersectionPnt, vrt1, vrt2); + // compute edgeLength: + edgeLength = VecDistance(aaPos[vrt1], intersectionPnt); + } + + grad_edge = grad_edge / edgeLength; + // set local maximum over all gradients along the edge to a given vertex + DoFRef(grad, vInd1[0]) = std::max(DoFRef(grad, vInd1[0]), grad_edge); + DoFRef(grad, vInd2[0]) = std::max(DoFRef(grad, vInd2[0]), grad_edge); + + } // end edge-loop + } else { + for (size_t e = 0; e < vEdges.size(); ++e) { + Edge* edge = vEdges[e]; + std::vector vVertexEdge; + CollectVertices(vVertexEdge, *m_spCutElementHandler->m_spMG, + edge); + if (vVertexEdge.size() != 2) + UG_THROW( + "error in collecting vertices associated to an edge!....EXIT!...\n"); + + Vertex* vrt1 = vVertexEdge[0]; + Vertex* vrt2 = vVertexEdge[1]; + if (dd->inner_dof_indices(vrt1, dim, vInd1) != 1) + UG_THROW("MovingParticle::compute_gradient(): Only one index expected."); + if (dd->inner_dof_indices(vrt2, dim, vInd2) != 1) + UG_THROW("MovingParticle::compute_gradient(): Only one index expected."); + + // compute local gradient along the edge + number edgeLength = VecDistance(aaPos[vrt1], aaPos[vrt2]); + number grad_edge = fabs( + DoFRef(sol, vInd1[0]) - DoFRef(sol, vInd2[0])); + grad_edge = grad_edge / edgeLength; + + // set local maximum over all gradients along the edge to a given vertex + DoFRef(grad, vInd1[0]) = std::max(DoFRef(grad, vInd1[0]), grad_edge); + DoFRef(grad, vInd2[0]) = std::max(DoFRef(grad, vInd2[0]), grad_edge); + + } // end edge-loop + + } + + } // end elem-loop + +} + +template +void MovingParticle:: +adjust_global_solution(vector_type& u, const int topLevel) +{ +// get data + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + +// loop all particles + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) + { + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order + // to indicate, whether a particle lies on a processor or not + std::vector ElemList = m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 MovingParticle::adjust_global_solution: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 MovingParticle::adjust_global_solution: ElemList.size(): " + << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + + MathVector transSol = m_spCutElementHandler->get_transSol(p,0); + MathVector rotSol = m_spCutElementHandler->get_rotSol(p, 0); + + ConstSmartPtr dd = this->m_spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + typename TDomain::position_accessor_type aaPos = m_spCutElementHandler->m_aaPos; + typedef typename std::vector::iterator ListIter; + + const MathVector& center = m_spCutElementHandler->get_center(p); + + + // loop all elements relevant for p-th particle + std::vector ElemListLog = m_spCutElementHandler->m_vvvElemListOutside[levIndex][p]; + + for (ListIter listIter = ElemListLog.begin(); listIter != ElemListLog.end(); ++listIter) + { + // get element + grid_base_object* elem = *listIter; + + // collect all vertices of the element + std::vector vVertex; + CollectVertices(vVertex, *m_spCutElementHandler->m_spMG, elem); + + // loop vertices + for (size_t v = 0; v < vVertex.size(); ++v) { + // get vertex + Vertex* vrt = vVertex[v]; + + for (size_t fct = 0; fct < dim; ++fct) { + // create multi index + std::vector < DoFIndex > vInd; + // get multi indices + if (dd->inner_dof_indices(vrt, fct, vInd) != 1) + UG_THROW("Only one index expected."); + + // set solution: particle velocity + MathVector radialCo; + VecSubtract(radialCo, aaPos[vrt], center); + MathMatrix rotationMatCo = + m_spCutElementHandler->get_rotationMat( + radialCo); + + // set solution: u = U + w x r + DoFRef(u, vInd[0]) = transSol[fct]; + for (int d = 0; d < dim; ++d) + DoFRef(u, vInd[0]) += rotationMatCo[fct][d] * rotSol[d]; + + // DoFRef(u, vInd[0]) = 0.0; + } + } // end vrt-loop + } // end outsideElem-loop + + } // end prt-loop + +} + +template +void MovingParticle:: +fill_freed_nodes(vector_type& u, const int topLevel, const number time) +{ +// get data + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + +// loop all particles + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) + { + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order + // to indicate, whether a particle lies on a processor or not + std::vector ElemList = + m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 MovingParticle::fill_freed_nodes: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 MovingParticle::fill_freed_nodes: ElemList.size(): " + << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + + ConstSmartPtr dd = this->m_spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + typename TDomain::position_accessor_type aaPos = m_spCutElementHandler->m_aaPos; + typedef typename std::vector::iterator ListIter; + + const MathVector& center = m_spCutElementHandler->get_center(p); + + MathVector transSol = m_spCutElementHandler->get_transSol(p,0); + MathVector rotSol = m_spCutElementHandler->get_rotSol(p, 0); + + + // loop all cut elements relevant for p-th particle + std::vector ElemListLog = m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + + for (ListIter listIter = ElemListLog.begin(); + listIter != ElemListLog.end(); ++listIter) + { + // get element + grid_base_object* elem = *listIter; + + // collect all vertices of the element + std::vector vVertex; + CollectVertices(vVertex, *m_spCutElementHandler->m_spMG, elem); + + // loop vertices + for (size_t v = 0; v < vVertex.size(); ++v) { + // get vertex + Vertex* vrt = vVertex[v]; + + // ---> is_insideParticle_with_given_index() is a geometrical check w.r.t NEW particle coords + // (update_prtCoords() needs to be called BEFORE calling this method!) + // ---> m_spOutsideMarker was marked w.r.t OLD particle coords + // (update_interface_data() has to be called AFTER calling this method!) + // ===> !is_inside && is_outside = is_freed :-) + if ( m_spCutElementHandler->is_insideParticle_with_given_index(p, vrt) + && m_spCutElementHandler->m_spOutsideMarker->is_marked(vrt) ) + { + for (size_t fct = 0; fct < dim; ++fct) { + // create multi index + std::vector < DoFIndex > vInd; + // get multi indices + if (dd->inner_dof_indices(vrt, fct, vInd) != 1) + UG_THROW("Only one index expected."); + + // set solution: particle velocity + MathVector radialCo; + VecSubtract(radialCo, aaPos[vrt], center); + MathMatrix rotationMatCo = m_spCutElementHandler->get_rotationMat(radialCo); + + // set solution + DoFRef(u, vInd[0]) = transSol[fct]; + for (int d = 0; d < dim; ++d) + DoFRef(u, vInd[0]) += rotationMatCo[fct][d] * rotSol[d]; + + + } + // if vertex lies outside particle and NOT near interface, i.e. !FlatTopVrtMarker = true, + // then the particle moved to fast in the last timestep + // ==> CFL criterion not satisfied + if (!m_spCutElementHandler->m_spInterfaceVrtMarker->is_marked(vrt) + && m_spCutElementHandler->m_spOutsideMarker->is_marked(vrt)) + UG_THROW("in 'fill_freed_nodes()': CFL criterion not satisfied!\n"); + } + + } // end vrt-loop + } // end cutElem-loop + + } // end prt-loop + +} + + +// call via lua BEFORE applying 'movingParticle:update()', which changes the global indices of trandSol/rotSol +// necessary for re-writing solution to NEW indices of vector 'u' during 'update_solution' +// => handling instance/buffering data := m_spCutElementHandler->(m_vSolTrans/m_vSolRot) + +// transfer direction: m_vSolTrans ---> DoFRef(u, transInd) +template +void MovingParticle:: +write_particle_velocity(vector_type& u, const int topLevel) +{ + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) + { +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order to indicate, + // whether a particle lies on a processor or not + std::vector ElemList = + m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 MovingParticle::write_particle_velocity: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 MovingParticle::write_particle_velocity: ElemList.size(): " + << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + + // 'get_transInd()' returns allready updated indices (updated during 'movingParticle:update()' via lua) + std::vector < DoFIndex > transInd = m_spCutElementHandler->get_transInd(levIndex, p); + std::vector < DoFIndex > rotInd = m_spCutElementHandler->get_rotInd(levIndex, p); + +// UG_LOG("in 'write_particle_velocity()': transInd = " << transInd[0] << "\n"); +// UG_LOG("in 'write_particle_velocity()': rotInd = " << rotInd[0] << "\n"); + + for (int d = 0; d < dim; ++d) + { + // write solution to the GridFunction 'u' into the vertices, where their DoFs were allocated + DoFRef(u, transInd[d]) = m_spCutElementHandler->get_transSol(p,0)[d]; + DoFRef(u, rotInd[d]) = m_spCutElementHandler->get_rotSol(p, 0)[d]; + + } + + } // end particle loop + +} + + +template +bool MovingParticle:: +mark_collision_area(SmartPtr refiner, int level) +{ + + typedef typename GeomObjBaseTypeByDim::base_obj_type elem_t; + typedef typename std::map::iterator ElemMapIterator; + typedef typename geometry_traits::iterator ElemIterator; + + bool elem_is_cut_by_2 = false; + + std::map num_cuts; + +// access the grid and the position attachment + MultiGrid* mg = (refiner->multi_grid()); + + for (ElemIterator elemIter = mg->begin(); elemIter != mg->end(); ++elemIter) { + elem_t* elem = *elemIter; + for (size_t p = 0; p < m_spCutElementHandler->num_particles(); ++p) { + //std::vector ElemListLog = m_vvvElemListCut[level][p]; + //for (ElemIterator elem = ElemListLog.begin(); + //elem != ElemListLog.end(); ++elem) { + bool cut = false; + for (size_t v = 0; v < elem->num_vertices(); ++v) { + const MathVector& vrtPos = m_spCutElementHandler->m_aaPos[elem->vertex(v)]; + const MathVector& center = m_spCutElementHandler->get_center(p); + const number radius = m_spCutElementHandler->get_radius(p); + + if (VecDistance(vrtPos, center) <= radius) { + cut = true; + } + } + if (cut) { + num_cuts[elem]++; + } + } + } + for (ElemMapIterator cut_elem = num_cuts.begin(); cut_elem != num_cuts.end(); ++cut_elem) { + if (cut_elem->second >= 2 && !mg->has_children(cut_elem->first)) { + refiner->mark(cut_elem->first); + elem_is_cut_by_2 = true; + } + } + return elem_is_cut_by_2; + } + +template +bool MovingParticle::estimate_repulsive_force_parameters(vector_type& u, int topLevel, double maxElemDiameter, double deltaT){ + // Get and synchronize velocities + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + + std::vector > transVel; + int num_particles = m_spCutElementHandler->num_particles(); + transVel.resize(num_particles); + + bool verbose = true; + +#ifdef UG_PARALLEL + pcl::ProcessCommunicator com; + + for (size_t p = 0; p < num_particles; ++p) { + MathVector transVelP; + + std::vector ElemList = + m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + if (ElemList.size() == 0) { + for (int d = 0; d < dim; ++d) { + transVelP[d] = 0.0; + } + } else { + std::vector < DoFIndex > transInd = + m_spCutElementHandler->get_transInd(levIndex, p); + + for (int d = 0; d < dim; ++d) { + transVelP[d] = DoFRef(u, transInd[d]); + } + + } + if (verbose) { + UG_LOG(pcl::ProcRank()<< "sends (" << transVelP[0] << "," << transVelP[1] << ")\n"); + } + com.allreduce(&transVelP[0],&transVel[p][0], dim, MPI_DOUBLE, PCL_RO_SUM); + if (verbose) { + if (dim == 1) { + UG_LOG("transVel[p] = ("<< transVel[p][0] <<")\n"); + } else{ + if (dim == 2) { + UG_LOG("transVel[p] = ("<< transVel[p][0] << "," << transVel[p][1] <<")\n"); + } else { + if (dim == 3) { + UG_LOG("transVel[p] = ("<< transVel[p][0] << "," << transVel[p][1] << "," << transVel[p][2] <<")\n"); + } + } + } + } + } +#else + for (size_t p = 0; p < num_particles; ++p) { + std::vector < DoFIndex > transInd = + m_spCutElementHandler->get_transInd(levIndex, p); + + for (int d = 0; d < dim; ++d) { + transVelP[d] = DoFRef(u, transInd[d]); + } + } +#endif + + std::vector > eps; + for (size_t i = 0; i < num_particles; ++i) { + eps.push_back(MathVector(0.0)); + } + bool conflict = true; + //const int levIndex = get_Index(GridLevel(13957 /*topLevel*/, GridLevel::LEVEL)); + + std::vector > centers; + for (size_t i = 0; i < num_particles; ++i) { + centers.push_back(m_spCutElementHandler->get_center(i)); + VecScaleAdd(centers[i], 1.0, centers[i], deltaT, transVel[i]); + } + + if (num_particles == 1) { + return false; + MathVector center_p = m_spCutElementHandler->get_center(0); + MathVector center_q; + center_q = center_p; + if (dim == 3){ + center_q[2] = m_spCutElementHandler->get_radius(0); + } else { + return false; + } + + center_p -= center_q; + UG_LOG("center diff is " << center_p << "\n"); + MathVector force; + + //VecScale(force, center_p, 1/(m_dt*m_dt)); + eps[0] = center_p; + if (center_p[2] < 0){ + if (verbose) { + UG_LOG("Giving " << eps[0] << " to set_minimum_correction_force\n"); + } + m_spInterfaceMapper->set_minimum_correction_force(true, eps); + return true; + } + return false; + } + + int iter = 25; + while (conflict && iter > 0) { + conflict = false; + std::vector > new_eps(eps); + for (size_t p = 0; p < num_particles; ++p){ + // Get values for particle p + number Mass_p = m_spInterfaceMapper->Mass(topLevel,p); + MathVector center_p; + VecAdd(center_p, centers[p], eps[p]); + number radius_p = m_spCutElementHandler->get_radius(p); + for (size_t q = 0; q < num_particles; ++q){ + if (p == q) { + continue; + } + if (verbose) + UG_LOG("p = "<Mass(topLevel,q); + MathVector center_q; + VecAdd(center_q, centers[q], eps[q]); + number radius_q = m_spCutElementHandler->get_radius(q); + + // Calculate force + if (verbose) { + if (dim == 1) { + UG_LOG("center_p = ("<< center_p[0] <<")\n"); + } else{ + if (dim == 2) { + UG_LOG("center_p = ("<< center_p[0] << "," << center_p[1] <<")\n"); + } else { + if (dim == 3) { + UG_LOG("center_p = ("<< center_p[0] << "," << center_p[1] << "," << center_p[2] <<")\n"); + } + } + } + if (dim == 1) { + UG_LOG("center_q = ("<< center_q[0] <<")\n"); + } else{ + if (dim == 2) { + UG_LOG("center_q = ("<< center_q[0] << "," << center_q[1] <<")\n"); + } else { + if (dim == 3) { + UG_LOG("center_q = ("<< center_q[0] << "," << center_q[1] << "," << center_q[2] <<")\n"); + } + } + } + } + + number centerDist = VecDistance(center_q, center_p); + if (verbose) + UG_LOG("centerDist = "<< centerDist <<"\n"); + number s_ij = centerDist - radius_p - radius_q; + if (verbose) + UG_LOG("centerDist - radius_p - radius_q = "<< s_ij <<"\n"); + UG_LOG("maxElemDiameter = "< 0.0) { + // Calculate distance vector between center points + double dist_norm = 0; + for (size_t d = 0; d < dim; ++d){ + dist_norm += (center_q[d]-center_p[d])*(center_q[d]-center_p[d]); + } + dist_norm = sqrt(dist_norm); + MathVector distanceVec = center_p; + distanceVec -= center_q; + distanceVec /= dist_norm; + + Delta_r *= Mass_q/(Mass_q+Mass_p); + + for (size_t d = 0; d < dim; ++d){ + new_eps[p][d] += 1.05*Delta_r*distanceVec[d]; + } + UG_LOG("new_eps[p]: " << new_eps[p] << "\n"); + conflict = true; + } + } + } + eps = new_eps; + iter--; + } + if (verbose) { + for (size_t i = 0; i < num_particles; ++i) { + if (dim == 2) { + UG_LOG("eps "< (" << eps[i][0] << "," << eps[i][1] << ")\n"); + } + if (dim == 3) { + UG_LOG("eps "< (" << eps[i][0] << "," << eps[i][1] << "," << eps[i][2] << ")\n"); + } + } + } + for (size_t i = 0; i < num_particles; ++i) { + double vecLen = VecLengthSq(eps[i]); + if (vecLen > 1e-14 or vecLen < -1e-14) { + m_spInterfaceMapper->set_minimum_correction_force(true, eps); + return true; + } + } + return false; +} + +#ifdef UG_PARALLEL +template +void MovingParticle:: +pre_balancing_update(vector_type& u, SmartPtr > spApproxSpace, + const int baseLevel, const int topLevel, const number time, number deltaT) +{ + int topLev = spApproxSpace->num_levels()-1; + if ( topLev != topLevel ) + UG_THROW("NavierStokes::update: parameter 'topLevel' = " << topLevel << " != " + << topLev << "current top leven! \n"); + + const char* filename = "update_times"; + std::string name(filename); + char ext[50]; + sprintf(ext, "_%d.txt", pcl::ProcRank()); + //sprintf(ext, ".txt"); + name.append(ext); + FILE* outputFile = fopen(name.c_str(), "a"); + + std::clock_t begin = std::clock(); + // A. copy solution to data: DoFRef(u, transInd) ---> m_vSolTrans + // reverse direction done during 'NavierStokes::write_particle_velocity()' + store_particle_velocity(u, topLevel); + std::clock_t end = std::clock(); + fprintf(outputFile,"store_particle_velocity %f", double(end - begin) / CLOCKS_PER_SEC); + + begin = std::clock(); + + // C. calculate new particle coordinates + m_spCutElementHandler->update_prtCoords(topLevel, deltaT); + end = std::clock(); + fprintf(outputFile,"update_prtCoords %f", double(end - begin) / CLOCKS_PER_SEC); + + begin = std::clock(); + + // B. synchronize particle data when everything in ParticleProvider is up to date. + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + m_spCutElementHandler->synchronize_particles(levIndex); + + end = std::clock(); + fprintf(outputFile,"synchronize_particles %f", double(end - begin) / CLOCKS_PER_SEC); + + begin = std::clock(); + + // D. fill particle nodes with their real solution + // --> FIRST store_particle_velocity() necessary, since eventually during + // fill extraDoF-nodes will be overwriten + fill_freed_nodes(u, topLevel, time); + end = std::clock(); + fprintf(outputFile,"fill_freed_nodes %f", double(end - begin) / CLOCKS_PER_SEC); + + fclose(outputFile); + +} + + +template +void MovingParticle:: +post_balancing_update(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, const int topLevel, const number time, number deltaT) +{ + // E. update data => new node for (u, transInd) ! + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + m_spCutElementHandler->template init(dd, baseLevel, topLevel); + + // F. update solution from data: m_vSolTransDoFRef(u, transInd) + // transfer direction: m_vSolTrans ---> DoFRef(u, transInd) + write_particle_velocity(u, topLevel); + + // reset volume: + m_spInterfaceMapper->reset_volume(); +} +#endif /* UG_PARALLEL */ + + + +} // end namespace NavierStokes +} // end namespace ug + +#endif /* MOVING_PARTICLE_IMPL_H_ */ diff --git a/incompressible/fv1/moving_particle/moving_particle_tools.h b/incompressible/fv1/moving_particle/moving_particle_tools.h new file mode 100644 index 0000000..6f84d12 --- /dev/null +++ b/incompressible/fv1/moving_particle/moving_particle_tools.h @@ -0,0 +1,290 @@ +/* + * moving_particle_tools.h + * + * Created on: 20.01.2015 + * Author: suze + */ + +#ifndef MOVING_PARTICLE_TOOLS_H_ +#define MOVING_PARTICLE_TOOLS_H_ + +namespace ug{ +namespace NavierStokes{ + + +template +void MovingParticle:: +interpolate_point(ConstSmartPtr dd, + const vector_type& u, + const MathVector& evalPos, // input data + MathVector& interpolation) // output data +{ + MathVector localPos(0.0); + +// get data + typename TDomain::position_accessor_type aaPos = this->m_spApproxSpace->domain()->position_accessor(); + + // create Function Group + FunctionGroup fctGrp(dd->function_pattern(), TokenizeString(m_spCutElementHandler->m_fctNames)); + + // get iterators for all elems on subset + typedef typename DoFDistribution::dim_traits::grid_base_object grid_base_object; + + typename DoFDistribution::traits::const_iterator iter, iterEnd; + iter = dd->begin(); + iterEnd = dd->end(); + + +// loop elements in order to compute 'U_global' and 'omega_global': + for( ; iter != iterEnd; ++iter) + { + // get element + grid_base_object* elem = *iter; + + if ( ContainsPoint(elem, evalPos, aaPos) ) + { + // get reference object id (i.e. Triangle, Quadrilateral, Tetrahedron, ...) + ReferenceObjectID roid = (ReferenceObjectID) elem->reference_object_id(); + + // get all corner coordinates + std::vector > vCorner; + CollectCornerCoordinates(vCorner, *elem, aaPos, true); + + // get the reference mapping for the element using global corners + DimReferenceMapping& mapping + = ReferenceMappingProvider::get(roid, vCorner); + + + // compute global integration points + mapping.global_to_local(localPos, evalPos); + + // loop all velocity components + for(int cmp = 0; cmp < dim+1; ++cmp) + { + // get fct id for compent + const size_t fct = fctGrp[cmp]; + + // local finite element id + const LFEID m_id = dd->local_finite_element_id(fct); + + // get trial space + const LocalShapeFunctionSet& rTrialSpace = + LocalFiniteElementProvider::get(roid, m_id); + + // number of dofs on element + const size_t num_sh = rTrialSpace.num_sh(); + + // get multiindices of element + std::vector ind; // aux. index array + dd->dof_indices(elem, fct, ind); + + // check multi indices + if(ind.size() != num_sh) + UG_THROW("L2ErrorIntegrand::evaluate: Wrong number of" + " multi indices."); + + // compute approximated solution at integration point + interpolation[cmp] = 0.0; + for(size_t sh = 0; sh < num_sh; ++sh) + { + // get value at shape point (e.g. corner for P1 fct) + const number valSH = DoFRef(u, ind[sh]); + //UG_LOG("interpolate: valSh = " << valSH << "\n"); + // add shape fct at ip * value at shape + interpolation[cmp] += valSH * rTrialSpace.shape(sh, localPos); + //UG_LOG("interpolate: interpolation[" << cmp << "] = " << interpolation[cmp] << "\n"); + + } + + } // end cmp-loop + } // end if-ContainsPoint + } // end element-loop + +} + + +template +void MovingParticle:: +print_pressure_nodal(const vector_type& u, const int topLevel) +{ + FILE *pressure; + char filename[40]; + sprintf(filename, "pressure_profile_level%d.txt", topLevel); + pressure = fopen(filename, "w"); + int numIter = 0; + number dist = 0.0; + + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + std::vector ElemListLog = m_spCutElementHandler->m_vvvElemListCut[levIndex][0]; + ConstSmartPtr dd = this->m_spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + typename TDomain::position_accessor_type aaPos = m_spCutElementHandler->m_aaPos; + + const MathVector& center = m_spCutElementHandler->get_center(0); + + typedef typename std::vector::iterator ListIter; + +// loop all cut elements in order to print pressure for fluid nodes, which lie near the interface + for(ListIter listIter = ElemListLog.begin(); + listIter != ElemListLog.end(); ++listIter) + { + numIter++; + // get element + grid_base_object* elem = *listIter; + + // collect all vertices of the element + std::vector vVertex; + CollectVertices(vVertex, *m_spCutElementHandler->m_spMG, elem); + + MathVector bufferVector; + VecSubtract(bufferVector, aaPos[vVertex[0]], aaPos[vVertex[2]]); + dist = 0.5*VecLength(bufferVector); + + // loop vertices + for(size_t v = 0; v < vVertex.size(); ++v) + { + // get vertex + Vertex* vrt = vVertex[v]; + + // print value for vertices in the fluid and near the interface: + if ( !m_spCutElementHandler->is_outsideFluid(vrt) ) + { + std::vector vInd; + if(dd->inner_dof_indices(vrt, dim, vInd) != 1) + UG_THROW("Only one index expected."); + + MathVector radialVector; + radialVector[0] = aaPos[vrt][0]-center[0]; + radialVector[1] = aaPos[vrt][1]-center[1]; + + number teta = atan2(aaPos[vrt][1]-center[1], aaPos[vrt][0]-center[0]); + if ( teta < 0.0 ) + teta += 2*3.1415926; + + fprintf(pressure, "%e \t %e \t %lu # teta, value pressure, KnotenIndex vInd[0] \n", teta, DoFRef(u, vInd[0]), vInd[0][0]); + + } + } + + } + + + fclose(pressure); + +} + +template +void MovingParticle:: +print_deltaP(const vector_type& u, const int topLevel) +{ + FILE *pressure; + char filename[40]; + sprintf(filename, "delta_p_level%d.txt", topLevel); + pressure = fopen(filename, "w"); + + ConstSmartPtr dd = this->m_spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + + MathVector printPos1; + MathVector printPos2; + printPos1[0] = 0.15; + printPos1[1] = 0.2; + printPos2[0] = 0.25; + printPos2[1] = 0.2; + + MathVector interpolVal1; + MathVector interpolVal2; + + interpolate_point(dd, u, printPos1, interpolVal1); + interpolate_point(dd, u, printPos2, interpolVal2); + + number deltaP = interpolVal1[dim] - interpolVal2[dim]; + + fprintf(pressure, "%e \t %e \t %e \n# interpolVal1[dim], interpolVal2[dim], deltaP\n", interpolVal1[dim], interpolVal2[dim], deltaP); + fclose(pressure); + +} + +template +void MovingParticle:: +print_pressure_teta(const vector_type& u, const int topLevel) +{ + FILE *pressure; + char filename[40]; + sprintf(filename, "pressure_teta_level%d.txt", topLevel); + pressure = fopen(filename, "w"); + + ConstSmartPtr dd = this->m_spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + const MathVector& center = m_spCutElementHandler->get_center(0); + + size_t t_max = 500; + MathVector printPos; + MathVector interpolVal; + for(size_t t = 0; t < t_max; ++t) + { + number teta = t*(2*PI/t_max); + number co = cos(teta); + number si = sin(teta); + printPos[0] = 0.2*co + center[0]; + printPos[1] = 0.2*si + center[1]; + interpolate_point(dd, u, printPos, interpolVal); + + //UG_LOG("after: interpolVal[" << dim<< "] = " << interpolVal[dim] << "\n"); + fprintf(pressure, "%e \t", teta); + fprintf(pressure, "%e \t", interpolVal[dim]); + fprintf(pressure, "%e \t %e # teta, value_pressure, printPos[0], printPot[1]\n", printPos[0], printPos[1]); + } + + fclose(pressure); + +} + +template +void MovingParticle:: +print_velocity(const vector_type& u, const int topLevel, number time, const char* filename) +{ +// Parameter von class 'MovingParticle' extrahieren: + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + const bool isTimedep = is_time_dependent(); + + size_t numPrt = m_spCutElementHandler->num_particles(); + + for(size_t p = 0; p < numPrt; ++p) + { + +#ifdef UG_PARALLEL + // use size of member 'CutElementHandler_FlatTop::m_vvvElemListCut' in order to indicate, + // whether a particle lies on a processor or not + std::vector ElemList = m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + UG_LOG("1 MovingParticle::output_velocity: ElemList.size(): " << ElemList.size() << "\n"); + if (ElemList.size() == 0) { + UG_LOG("2 MovingParticle::output_velocity: ElemList.size(): " << ElemList.size() << " => skip assembling! \n"); + continue; + } +#endif + + // Parameter von class 'CutElementHandler' extrahieren: + + // get multiindices for translation and rotation of particle + std::vector < DoFIndex > transInd = m_spCutElementHandler->get_transInd(levIndex, p); + std::vector < DoFIndex > rotInd = m_spCutElementHandler->get_rotInd(levIndex, p); + + MathVector transSol; + MathVector rotSol; + + for ( int d = 0; d < dim; ++d ) + { + transSol[d] = DoFRef(u, transInd[d]); + rotSol[d] = DoFRef(u, rotInd[d]); + } + + // auf untergeordnete class 'ParticleProvider' weiterleiten: + m_spCutElementHandler->print_velocity(transSol, rotSol, p, isTimedep, time, filename); + } + +} + +} // end namespace NavierStokes +} // end namespace ug + + + +#endif /* MOVING_PARTICLE_TOOLS_H_ */ diff --git a/incompressible/fv1/navier_stokes_fv1.cpp b/incompressible/fv1/navier_stokes_fv1.cpp index f93307e..0a31f73 100644 --- a/incompressible/fv1/navier_stokes_fv1.cpp +++ b/incompressible/fv1/navier_stokes_fv1.cpp @@ -33,9 +33,15 @@ #include "navier_stokes_fv1.h" #include "lib_disc/spatial_disc/disc_util/fv1_geom.h" +//#include "lib_disc/spatial_disc/disc_util/fv1FT_geom.h" #include "lib_disc/spatial_disc/disc_util/geom_provider.h" +//#include "moving_particle/interface_handler_particle.h" namespace ug{ + +//template +//class InterfaceHandlerLocalParticle; + namespace NavierStokes{ //////////////////////////////////////////////////////////////////////////////// @@ -129,6 +135,7 @@ set_source(SmartPtr, dim> > data) m_imSourceSCVF.set_data(data); } + //////////////////////////////////////////////////////////////////////////////// // assembling functions //////////////////////////////////////////////////////////////////////////////// @@ -181,7 +188,7 @@ prep_elem_loop(const ReferenceObjectID roid, const int si) " Density has not been set, but is required."); // set local positions for imports - if(!TFVGeom::usesHangingNodes) + if(!TFVGeom::usesHangingNodes) { static const int refDim = TElem::dim; TFVGeom& geo = GeomProvider::get(); @@ -198,6 +205,7 @@ prep_elem_loop(const ReferenceObjectID roid, const int si) } + template template void NavierStokesFV1:: @@ -281,6 +289,8 @@ add_jac_A_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, const Mat // interpolate velocity at ip with standard lagrange interpolation static const size_t numSCVF = TFVGeom::numSCVF; +// static const size_t numSCVF = TFVGeom::maxNumSCVF; + MathVector StdVel[numSCVF]; for(size_t ip = 0; ip < geo.num_scvf(); ++ip) { @@ -599,8 +609,6 @@ template void NavierStokesFV1:: add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) { - //UG_LOG("Anfang add_def_A_elem"); - // Only first order implemented UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); @@ -629,7 +637,10 @@ add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const Mat } // interpolate velocity at ip with standard lagrange interpolation - static const size_t numSCVF = TFVGeom::numSCVF; + + static const size_t numSCVF = TFVGeom::numSCVF; + // static const size_t numSCVF = TFVGeom::maxNumSCVF; + MathVector StdVel[numSCVF]; for(size_t ip = 0; ip < geo.num_scvf(); ++ip) { @@ -691,9 +702,10 @@ add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const Mat gradVel(d1, d2) = 0.0; for(size_t sh = 0; sh < scvf.num_sh(); ++sh) gradVel(d1, d2) += scvf.global_grad(sh)[d2] - * u(d1, sh); + * u(d1, sh); } + // 2. Compute flux MathVector diffFlux; @@ -1008,10 +1020,15 @@ template<> void NavierStokesFV1:: register_all_funcs(bool bHang) { + bool bRegisterUsual = true; + // switch assemble functions if(!bHang) { - register_func >(); + if ( bRegisterUsual ) + register_func >(); + // else + // register_func > >(); } else { @@ -1025,11 +1042,18 @@ template<> void NavierStokesFV1:: register_all_funcs(bool bHang) { + bool bRegisterUsual = true; + // switch assemble functions if(!bHang) { - register_func >(); - register_func >(); + if ( bRegisterUsual ) + { + register_func >(); + register_func >(); + } + // else + // register_func > >(); } else { @@ -1043,19 +1067,28 @@ template<> void NavierStokesFV1:: register_all_funcs(bool bHang) { + bool bRegisterUsual = true; + // switch assemble functions if(!bHang) { - register_func >(); - register_func >(); - register_func >(); - register_func >(); + if ( bRegisterUsual ) + { + register_func >(); + register_func >(); + register_func >(); + register_func >(); + } + // else + // register_func > >(); } else { UG_THROW("NavierStokesFV1: Hanging Nodes not implemented.") } + } + #endif template diff --git a/incompressible/fv1/navier_stokes_fv1.h b/incompressible/fv1/navier_stokes_fv1.h index d64f37a..b0b3d38 100644 --- a/incompressible/fv1/navier_stokes_fv1.h +++ b/incompressible/fv1/navier_stokes_fv1.h @@ -30,6 +30,7 @@ * GNU Lesser General Public License for more details. */ + #ifndef __H__UG__PLUGINS__NAVIER_STOKES__INCOMPRESSIBLE__FV1__NAVIER_STOKES_FV1__ #define __H__UG__PLUGINS__NAVIER_STOKES__INCOMPRESSIBLE__FV1__NAVIER_STOKES_FV1__ @@ -47,8 +48,10 @@ #include "stabilization.h" namespace ug{ + namespace NavierStokes{ + /// \ingroup lib_disc_elem_disc /// @{ @@ -563,6 +566,9 @@ class NavierStokesFV1 virtual void init(); protected: + /// current shape function set (needed for GeomProvider::get()) + LFEID m_LFEID; + /// register utils /// \{ virtual void register_all_funcs(bool bHang); diff --git a/incompressible/fv1/navier_stokes_fv1_cutElem.cpp b/incompressible/fv1/navier_stokes_fv1_cutElem.cpp new file mode 100644 index 0000000..44cbfcc --- /dev/null +++ b/incompressible/fv1/navier_stokes_fv1_cutElem.cpp @@ -0,0 +1,1128 @@ +/* + * Copyright (c) 2010-2014: G-CSC, Goethe University Frankfurt + * Author: Andreas Vogel + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#include "navier_stokes_fv1_cutElem.h" + +#include "lib_disc/spatial_disc/disc_util/fv1FT_geom.h" +#include "lib_disc/spatial_disc/disc_util/geom_provider.h" + +namespace ug{ + + +namespace NavierStokes{ + +//////////////////////////////////////////////////////////////////////////////// +// Constructor - set default values +//////////////////////////////////////////////////////////////////////////////// + + +template +NavierStokesFV1_cutElem::NavierStokesFV1_cutElem(const char* functions, + const char* subsets) +: IncompressibleNavierStokesBase(functions, subsets) +{ + init(); +}; + +template +NavierStokesFV1_cutElem::NavierStokesFV1_cutElem(const std::vector& vFct, + const std::vector& vSubset) +: IncompressibleNavierStokesBase(vFct, vSubset), + m_bCutElemGeom(false) +{ + init(); +}; + + +template +void NavierStokesFV1_cutElem::init() +{ +// check number of functions + if(this->num_fct() != dim+1) + UG_THROW("Wrong number of functions: The ElemDisc 'NavierStokes'" + " needs exactly "<register_import(m_imSourceSCV); + this->register_import(m_imSourceSCVF); + this->register_import(m_imKinViscosity); + this->register_import(m_imDensitySCVF); + this->register_import(m_imDensitySCV); + + m_imSourceSCV.set_rhs_part(); + m_imSourceSCVF.set_rhs_part(); + m_imDensitySCV.set_mass_part(); + + // default value for density + base_type::set_density(1.0); + + // update assemble functions + register_all_funcs(false); + +} + +template +void NavierStokesFV1_cutElem:: +prepare_setting(const std::vector& vLfeID, bool bNonRegularGrid) +{ + if(bNonRegularGrid) + UG_THROW("NavierStokes: only regular grid implemented."); + +// check number + if(vLfeID.size() != dim+1) + UG_THROW("NavierStokes: Need exactly "< +void NavierStokesFV1_cutElem:: +set_kinematic_viscosity(SmartPtr > data) +{ + m_imKinViscosity.set_data(data); +} + +template +void NavierStokesFV1_cutElem:: +set_density(SmartPtr > data) +{ + m_imDensitySCVF.set_data(data); + m_imDensitySCV.set_data(data); +} + +template +void NavierStokesFV1_cutElem:: +set_source(SmartPtr, dim> > data) +{ + m_imSourceSCV.set_data(data); + m_imSourceSCVF.set_data(data); +} + +//////////////////////////////////////////////////////////////////////////////// +// assembling functions +//////////////////////////////////////////////////////////////////////////////// + +template +template +void NavierStokesFV1_cutElem:: +prep_elem_loop(const ReferenceObjectID roid, const int si) +{ +// Only first order implementation + if(!(TFVGeom::order == 1)) + UG_THROW("Only first order implementation, but other Finite Volume" + " Geometry set."); + +// check, that stabilization has been set + if(m_spStab.invalid()) + UG_THROW("Stabilization has not been set."); + +// init stabilization for element type + m_spStab->template set_geometry_type(); + + if (! m_bStokes) // no convective terms in the Stokes eq. => no upwinding + { + // check, that convective upwinding has been set + if(m_spConvStab.invalid() && m_spConvUpwind.invalid()) + UG_THROW("Upwinding for convective Term in Momentum eq. not set."); + + // init convection stabilization for element type + if(m_spConvStab.valid()) + m_spConvStab->template set_geometry_type(); + + // init convection stabilization for element type + if(m_spConvUpwind.valid()) + m_spConvUpwind->template set_geometry_type(); + } + +// check, that kinematic Viscosity has been set + if(!m_imKinViscosity.data_given()) + UG_THROW("NavierStokes::prep_elem_loop:" + " Kinematic Viscosity has not been set, but is required."); + +// check, that Density has been set + if(!m_imDensitySCVF.data_given()) + UG_THROW("NavierStokes::prep_elem_loop:" + " Density has not been set, but is required."); + +// check, that Density has been set + if(!m_imDensitySCV.data_given()) + UG_THROW("NavierStokes::prep_elem_loop:" + " Density has not been set, but is required."); + +// set local positions for imports + //if(!TFVGeom::usesHangingNodes) + if(!TFVGeom::usesHangingNodes && TFVGeom::staticLocalData) + { + static const int refDim = TElem::dim; + TFVGeom& geo = GeomProvider::get(); + const MathVector* vSCVFip = geo.scvf_local_ips(); + const size_t numSCVFip = geo.num_scvf_ips(); + const MathVector* vSCVip = geo.scv_local_ips(); + const size_t numSCVip = geo.num_scv_ips(); + m_imKinViscosity.template set_local_ips(vSCVFip,numSCVFip); + m_imDensitySCVF.template set_local_ips(vSCVFip,numSCVFip); + m_imDensitySCV.template set_local_ips(vSCVip,numSCVip); + m_imSourceSCV.template set_local_ips(vSCVip,numSCVip); + m_imSourceSCVF.template set_local_ips(vSCVFip,numSCVFip); + } + +} + + +template +template +void NavierStokesFV1_cutElem:: +fsh_elem_loop() +{} + +template +template +void NavierStokesFV1_cutElem:: +prep_elem(const LocalVector& u, GridObject* elem, ReferenceObjectID roid, const MathVector vCornerCoords[]) +{ +// Update Geometry for this element + TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + try{ + geo.update(elem, vCornerCoords, &(this->subset_handler())); + } + UG_CATCH_THROW("NavierStokes::prep_elem:" + " Cannot update Finite Volume Geometry."); + +// set local positions for imports + //if(TFVGeom::usesHangingNodes) + if(TFVGeom::usesHangingNodes || !TFVGeom::staticLocalData) + { + // request ip series + static const int refDim = TElem::dim; + const MathVector* vSCVFip = geo.scvf_local_ips(); + const size_t numSCVFip = geo.num_scvf_ips(); + const MathVector* vSCVip = geo.scv_local_ips(); + const size_t numSCVip = geo.num_scv_ips(); + m_imKinViscosity.template set_local_ips(vSCVFip,numSCVFip); + m_imDensitySCVF.template set_local_ips(vSCVFip,numSCVFip); + m_imDensitySCV.template set_local_ips(vSCVip,numSCVip); + m_imSourceSCV.template set_local_ips(vSCVip,numSCVip); + m_imSourceSCVF.template set_local_ips(vSCVFip,numSCVFip); + + } + +// set global positions for imports + const MathVector* vSCVFip = geo.scvf_global_ips(); + const size_t numSCVFip = geo.num_scvf_ips(); + const MathVector* vSCVip = geo.scv_global_ips(); + const size_t numSCVip = geo.num_scv_ips(); + m_imKinViscosity.set_global_ips(vSCVFip, numSCVFip); + m_imDensitySCVF.set_global_ips(vSCVFip, numSCVFip); + m_imDensitySCV.set_global_ips(vSCVip, numSCVip); + m_imSourceSCV.set_global_ips(vSCVip, numSCVip); + m_imSourceSCVF.set_global_ips(vSCVFip, numSCVFip); +} + + +template +template +void NavierStokesFV1_cutElem:: +add_jac_A_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, + const MathVector vCornerCoords[]) +{ +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + static const TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + +// check for source term to pass to the stabilization + const DataImport, dim>* pSource = NULL; + if(m_imSourceSCVF.data_given()) pSource = &m_imSourceSCVF; + +// check for solutions to pass to stabilization in time-dependent case + const LocalVector *pSol = &u, *pOldSol = NULL; + number dt = 0.0; + + if(this->is_time_dependent()) + { + // get and check current and old solution + const LocalVectorTimeSeries* vLocSol = this->local_time_solutions(); + if(vLocSol->size() != 2) + UG_THROW("NavierStokes::add_jac_A_elem: " + " Stabilization needs exactly two time points."); + + // remember local solutions + pSol = &vLocSol->solution(0); + pOldSol = &vLocSol->solution(1); + dt = vLocSol->time(0) - vLocSol->time(1); + } + +// interpolate velocity at ip with standard lagrange interpolation + //static const size_t numSCVF = TFVGeom::numSCVF; + static const size_t numSCVF = TFVGeom::maxNumSCVF; + + MathVector StdVel[numSCVF]; + for(size_t ip = 0; ip < geo.num_scvf(); ++ip) + { + const typename TFVGeom::SCVF& scvf = geo.scvf(ip); + VecSet(StdVel[ip], 0.0); + + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + for(int d1 = 0; d1 < dim; ++d1) + StdVel[ip][d1] += u(d1, sh) * scvf.shape(sh); + } + +// compute stabilized velocities and shapes for continuity equation + m_spStab->update(&geo, *pSol, StdVel, m_bStokes, m_imKinViscosity, m_imDensitySCVF, pSource, pOldSol, dt); + + if (! m_bStokes) // no convective terms in the Stokes eq. => no upwinding + { + // compute stabilized velocities and shapes for convection upwind + if(m_spConvStab.valid()) + if(m_spConvStab != m_spStab) + m_spConvStab->update(&geo, *pSol, StdVel, false, m_imKinViscosity, m_imDensitySCVF, pSource, pOldSol, dt); + + // compute upwind shapes + if(m_spConvUpwind.valid()) + if(m_spStab->upwind() != m_spConvUpwind) + m_spConvUpwind->update(&geo, StdVel); + } + +// get a const (!!) reference to the stabilization + const INavierStokesFV1Stabilization& stab = *m_spStab; + const INavierStokesFV1Stabilization& convStab = *m_spConvStab; + const INavierStokesUpwind& upwind = *m_spConvUpwind; + +// loop Sub Control Volume Faces (SCVF) + for(size_t ip = 0; ip < geo.num_scvf(); ++ip) + { + // get current SCVF + const typename TFVGeom::SCVF& scvf = geo.scvf(ip); + + // loop shape functions + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + { + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + // Momentum Equation (conservation of momentum) + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + + //////////////////////////////////////////////////// + // Diffusive Term (Momentum Equation) + //////////////////////////////////////////////////// + + // Compute flux derivative at IP + const number flux_sh = -1.0 * m_imKinViscosity[ip] * m_imDensitySCVF[ip] + * VecDot(scvf.global_grad(sh), scvf.normal()); + + // Add flux derivative to local matrix + for(int d1 = 0; d1 < dim; ++d1) + { + J(d1, scvf.from(), d1, sh) += flux_sh; + J(d1, scvf.to() , d1, sh) -= flux_sh; + } + + if(!m_bLaplace) + { + for(int d1 = 0; d1 < dim; ++d1) + for(int d2 = 0; d2 < dim; ++d2) + { + const number flux2_sh = -1.0 * m_imKinViscosity[ip] * m_imDensitySCVF[ip] + * scvf.global_grad(sh)[d1] * scvf.normal()[d2]; + J(d1, scvf.from(), d2, sh) += flux2_sh; + J(d1, scvf.to() , d2, sh) -= flux2_sh; + } + } + + //////////////////////////////////////////////////// + // Pressure Term (Momentum Equation) + //////////////////////////////////////////////////// + + // Add flux derivative for local matrix + for(int d1 = 0; d1 < dim; ++d1) + { + const number flux_sh = scvf.shape(sh) * scvf.normal()[d1]; + J(d1, scvf.from(), _P_, sh) += flux_sh; + J(d1, scvf.to() , _P_, sh) -= flux_sh; + } + + //////////////////////////////////////////////////// + // Convective Term (Momentum Equation) + //////////////////////////////////////////////////// + + // note: StdVel will be the advecting velocity (not upwinded) + // UpwindVel/StabVel will be the transported velocity + + if (! m_bStokes) // no convective terms in the Stokes equation + { + // compute upwind velocity + MathVector UpwindVel; + + // switch PAC + if(m_spConvUpwind.valid()) UpwindVel = upwind.upwind_vel(ip, u, StdVel); + else if (m_spConvStab.valid()) UpwindVel = convStab.stab_vel(ip); + else UG_THROW("Cannot find upwind for convective term."); + + // peclet blend + number w = 1.0; + if(m_bPecletBlend) + w = peclet_blend(UpwindVel, geo, ip, StdVel[ip], m_imKinViscosity[ip]); + + // compute product of stabilized vel and normal + const number prod = VecProd(StdVel[ip], scvf.normal()) * m_imDensitySCVF[ip]; + + /////////////////////////////////// + // Add fixpoint linearization + /////////////////////////////////// + + // Stabilization used as upwind + if(m_spConvStab.valid()) + { + // velocity derivatives + if(stab.vel_comp_connected()) + for(int d1 = 0; d1 < dim; ++d1) + for(int d2 = 0; d2 < dim; ++d2) + { + const number convFlux_vel = prod * w * convStab.stab_shape_vel(ip, d1, d2, sh); + J(d1, scvf.from(), d2, sh) += convFlux_vel; + J(d1, scvf.to() , d2, sh) -= convFlux_vel; + } + else + for(int d1 = 0; d1 < dim; ++d1) + { + const number convFlux_vel = prod * w * convStab.stab_shape_vel(ip, d1, d1, sh); + J(d1, scvf.from(), d1, sh) += convFlux_vel; + J(d1, scvf.to() , d1, sh) -= convFlux_vel; + } + + // pressure derivative + for(int d1 = 0; d1 < dim; ++d1) + { + const number convFlux_p = prod * w * convStab.stab_shape_p(ip, d1, sh); + + J(d1, scvf.from(), _P_, sh) += convFlux_p; + J(d1, scvf.to() , _P_, sh) -= convFlux_p; + } + } + + // Upwind used as upwind + if(m_spConvUpwind.valid()) + { + number convFlux_vel = upwind.upwind_shape_sh(ip, sh); + + // in some cases (e.g. PositiveUpwind, RegularUpwind) the upwind + // velocity in an ip depends also on the upwind velocity in + // other ips. This is reflected by the fact, that the ip + // shapes are non-zero. In that case, we can interpolate an + // approximate upwind only from the corner velocities by using + // u_up = \sum shape_co U_co + \sum shape_ip \tilde{u}_ip + // = \sum shape_co U_co + \sum \sum shape_ip norm_shape_co|_ip * U_co + if(m_spConvUpwind->non_zero_shape_ip()) + { + for(size_t ip2 = 0; ip2 < geo.num_scvf(); ++ip2) + { + const typename TFVGeom::SCVF& scvf2 = geo.scvf(ip2); + convFlux_vel += scvf2.shape(sh) * upwind.upwind_shape_ip(ip, ip2); + } + } + + convFlux_vel *= prod * w; + + for(int d1 = 0; d1 < dim; ++d1) + { + J(d1, scvf.from(), d1, sh) += convFlux_vel; + J(d1, scvf.to() , d1, sh) -= convFlux_vel; + } + } + + // derivative due to peclet blending + if(m_bPecletBlend) + { + const number convFluxPe = prod * (1.0-w) * scvf.shape(sh); + for(int d1 = 0; d1 < dim; ++d1) + { + J(d1, scvf.from(), d1, sh) += convFluxPe; + J(d1, scvf.to() , d1, sh) -= convFluxPe; + } + } + + ///////////////////////////////////////// + // Add full jacobian (remaining part) + ///////////////////////////////////////// + + // Add remaining term for exact jacobian + if(m_bFullNewtonFactor) + { + // Stabilization used as upwind + if(m_spConvStab.valid()) + { + // loop defect components + for(int d1 = 0; d1 < dim; ++d1) + { + for(int d2 = 0; d2 < dim; ++d2) + { + // derivatives w.r.t. velocity + // Compute n * derivs + number prod_vel = 0.0; + + // Compute sum_j n_j * \partial_{u_i^sh} u_j + if(stab.vel_comp_connected()) + for(size_t k = 0; k < (size_t)dim; ++k) + prod_vel += w * convStab.stab_shape_vel(ip, k, d2, sh) + * scvf.normal()[k]; + else + prod_vel = convStab.stab_shape_vel(ip, d1, d1, sh) + * scvf.normal()[d1]; + + prod_vel *= m_bFullNewtonFactor * m_imDensitySCVF[ip]; + + J(d1, scvf.from(), d2, sh) += prod_vel * UpwindVel[d1]; + J(d1, scvf.to() , d2, sh) -= prod_vel * UpwindVel[d1]; + } + + // derivative w.r.t pressure + // Compute n * derivs + number prod_p = 0.0; + + // Compute sum_j n_j * \parial_{u_i^sh} u_j + for(int k = 0; k < dim; ++k) + prod_p += convStab.stab_shape_p(ip, k, sh) + * scvf.normal()[k]; + + prod_p *= m_bFullNewtonFactor * m_imDensitySCVF[ip]; + + J(d1, scvf.from(), _P_, sh) += prod_p * UpwindVel[d1]; + J(d1, scvf.to() , _P_, sh) -= prod_p * UpwindVel[d1]; + } + } + + // Upwind used as upwind + if(m_spConvUpwind.valid()) + { + // loop defect components + for(int d1 = 0; d1 < dim; ++d1) + for(int d2 = 0; d2 < dim; ++d2) + { + // derivatives w.r.t. velocity + number prod_vel = w * upwind.upwind_shape_sh(ip,sh) + * scvf.normal()[d2] * m_imDensitySCVF[ip]; + + J(d1, scvf.from(), d2, sh) += prod_vel * UpwindVel[d1]; + J(d1, scvf.to() , d2, sh) -= prod_vel * UpwindVel[d1]; + } + } + + // derivative due to peclet blending + if(m_bPecletBlend) + { + for(int d1 = 0; d1 < dim; ++d1) + for(int d2 = 0; d2 < dim; ++d2) + { + const number convFluxPe = UpwindVel[d1] * (1.0-w) + * scvf.shape(sh) + * scvf.normal()[d2] + * m_imDensitySCVF[ip]; + J(d1, scvf.from(), d2, sh) += convFluxPe; + J(d1, scvf.to() , d2, sh) -= convFluxPe; + } + } + } // end exact jacobian part + + } // end of if (! m_bStokes) for the convective terms + + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + // Continuity Equation (conservation of mass) + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + + // Add derivative of stabilized flux w.r.t velocity comp to local matrix + if(stab.vel_comp_connected()) + { + for(int d1 = 0; d1 < dim; ++d1) + { + number contFlux_vel = 0.0; + for(int d2 = 0; d2 < dim; ++d2) + contFlux_vel += stab.stab_shape_vel(ip, d2, d1, sh) + * scvf.normal()[d2] * m_imDensitySCVF[ip]; + + J(_P_, scvf.from(), d1, sh) += contFlux_vel; + J(_P_, scvf.to() , d1, sh) -= contFlux_vel; + } + } + else + { + for(int d1 = 0; d1 < dim; ++d1) + { + const number contFlux_vel = stab.stab_shape_vel(ip, d1, d1, sh) + * scvf.normal()[d1] * m_imDensitySCVF[ip]; + + J(_P_, scvf.from(), d1, sh) += contFlux_vel; + J(_P_, scvf.to() , d1, sh) -= contFlux_vel; + } + } + + // Add derivative of stabilized flux w.r.t pressure to local matrix + number contFlux_p = 0.0; + for(int d1 = 0; d1 < dim; ++d1) + contFlux_p += stab.stab_shape_p(ip, d1, sh) * scvf.normal()[d1] * m_imDensitySCVF[ip]; + + J(_P_, scvf.from(), _P_, sh) += contFlux_p; + J(_P_, scvf.to() , _P_, sh) -= contFlux_p; + } + } +} + +template +template +void NavierStokesFV1_cutElem:: +add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ +// Only first order implemented + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + //static const TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + TFVGeom& geo = GeomProvider::get(m_LFEID,1); + +// check for source term to pass to the stabilization + const DataImport, dim>* pSource = NULL; + if(m_imSourceSCVF.data_given()) pSource = &m_imSourceSCVF; + +// check for solutions to pass to stabilization in time-dependent case + const LocalVector *pSol = &u, *pOldSol = NULL; + number dt = 0.0; + if(this->is_time_dependent()) + { + // get and check current and old solution + const LocalVectorTimeSeries* vLocSol = this->local_time_solutions(); + if(vLocSol->size() != 2) + UG_THROW("NavierStokes::add_def_A_elem: " + " Stabilization needs exactly two time points."); + + // remember local solutions + pSol = &vLocSol->solution(0); + pOldSol = &vLocSol->solution(1); + dt = vLocSol->time(0) - vLocSol->time(1); + } + +// interpolate velocity at ip with standard lagrange interpolation + //static const size_t numSCVF = TFVGeom::numSCVF; + static const size_t numSCVF = TFVGeom::maxNumSCVF; + + MathVector StdVel[numSCVF]; + for(size_t ip = 0; ip < geo.num_scvf(); ++ip) + { + const typename TFVGeom::SCVF& scvf = geo.scvf(ip); + VecSet(StdVel[ip], 0.0); + + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + for(int d1 = 0; d1 < dim; ++d1) + StdVel[ip][d1] += u(d1, sh) * scvf.shape(sh); + } + +// compute stabilized velocities and shapes for continuity equation + // \todo: (optional) Here we can skip the computation of shapes, implement? + m_spStab->update(&geo, *pSol, StdVel, m_bStokes, m_imKinViscosity, m_imDensitySCVF, pSource, pOldSol, dt); + + if (! m_bStokes) // no convective terms in the Stokes eq. => no upwinding + { + // compute stabilized velocities and shapes for convection upwind + if(m_spConvStab.valid()) + if(m_spConvStab != m_spStab) + m_spConvStab->update(&geo, *pSol, StdVel, false, m_imKinViscosity, m_imDensitySCVF, pSource, pOldSol, dt); + + // compute upwind shapes + if(m_spConvUpwind.valid()) + if(m_spStab->upwind() != m_spConvUpwind) + m_spConvUpwind->update(&geo, StdVel); + } + +// get a const (!!) reference to the stabilization + const INavierStokesFV1Stabilization& stab = *m_spStab; + const INavierStokesFV1Stabilization& convStab = *m_spConvStab; + const INavierStokesUpwind& upwind = *m_spConvUpwind; + +// loop Sub Control Volume Faces (SCVF) + for(size_t ip = 0; ip < geo.num_scvf(); ++ip) + { + // get current SCVF + const typename TFVGeom::SCVF& scvf = geo.scvf(ip); + + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + // Momentum Equation (conservation of momentum) + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + + // \todo: we could also add all fluxes at once in order to save several + // accesses to the local defect, implement and loose clarity? + + //////////////////////////////////////////////////// + // Diffusive Term (Momentum Equation) + //////////////////////////////////////////////////// + + // 1. Interpolate Functional Matrix of velocity at ip + MathMatrix gradVel; + for(int d1 = 0; d1 < dim; ++d1) + for(int d2 = 0; d2 diffFlux; + + // Add (\nabla u) \cdot \vec{n} + MatVecMult(diffFlux, gradVel, scvf.normal()); + + // Add (\nabla u)^T \cdot \vec{n} + if(!m_bLaplace) + TransposedMatVecMultAdd(diffFlux, gradVel, scvf.normal()); + + // scale by viscosity + VecScale(diffFlux, diffFlux, (-1.0) * m_imKinViscosity[ip] * m_imDensitySCVF[ip]); + + // 3. Add flux to local defect + for(int d1 = 0; d1 < dim; ++d1) + { + d(d1, scvf.from()) += diffFlux[d1]; + d(d1, scvf.to() ) -= diffFlux[d1]; + } + + //////////////////////////////////////////////////// + // Convective Term (Momentum Equation) + //////////////////////////////////////////////////// + + // note: StdVel will be the advecting velocity (not upwinded) + // UpwindVel/StabVel will be the transported velocity + + if (! m_bStokes) // no convective terms in the Stokes equation + { + // find the upwind velocity at ip + MathVector UpwindVel; + + // switch PAC + if(m_spConvUpwind.valid()) UpwindVel = upwind.upwind_vel(ip, u, StdVel); + else if (m_spConvStab.valid()) UpwindVel = convStab.stab_vel(ip); + else UG_THROW("Cannot find upwind for convective term."); + + // Peclet Blend + if(m_bPecletBlend) + peclet_blend(UpwindVel, geo, ip, StdVel[ip], m_imKinViscosity[ip]); + + // compute product of standard velocity and normal + const number prod = VecProd(StdVel[ip], scvf.normal()) * m_imDensitySCVF[ip]; + + // Add contributions to local velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + d(d1, scvf.from()) += UpwindVel[d1] * prod; + d(d1, scvf.to() ) -= UpwindVel[d1] * prod; + } + + } + + //////////////////////////////////////////////////// + // Pressure Term (Momentum Equation) + //////////////////////////////////////////////////// + + // 1. Interpolate pressure at ip + number pressure = 0.0; + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + pressure += scvf.shape(sh) * u(_P_, sh); + + // 2. Add contributions to local defect + for(int d1 = 0; d1 < dim; ++d1) + { + d(d1, scvf.from()) += pressure * scvf.normal()[d1]; + d(d1, scvf.to() ) -= pressure * scvf.normal()[d1]; + } + + + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + // Continuity Equation (conservation of mass) + //////////////////////////////////////////////////// + //////////////////////////////////////////////////// + + // compute flux at ip + const number contFlux = VecProd(stab.stab_vel(ip), scvf.normal()) * m_imDensitySCVF[ip]; + + // Add contributions to local defect + d(_P_, scvf.from()) += contFlux; + d(_P_, scvf.to() ) -= contFlux; + + } + +} + + +template +template +void NavierStokesFV1_cutElem:: +add_jac_M_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + static const TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + +// loop Sub Control Volumes (SCV) + for(size_t ip = 0; ip < geo.num_scv(); ++ip) + { + // get SCV + const typename TFVGeom::SCV& scv = geo.scv(ip); + + // get associated node + const int sh = scv.node_id(); + + // loop velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + // Add to local matrix + J(d1, sh, d1, sh) += scv.volume() * m_imDensitySCV[ip]; + } + } +} + + +template +template +void NavierStokesFV1_cutElem:: +add_def_M_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]) +{ +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// get finite volume geometry + static const TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + +// loop Sub Control Volumes (SCV) + for(size_t ip = 0; ip < geo.num_scv(); ++ip) + { + // get current SCV + const typename TFVGeom::SCV& scv = geo.scv(ip); + + // get associated node + const int sh = scv.node_id(); + + // loop velocity components + for(int d1 = 0; d1 < dim; ++d1) + { + // Add to local matrix + d(d1, sh) += u(d1, sh) * scv.volume() * m_imDensitySCV[ip]; + } + } +} + + +template +template +void NavierStokesFV1_cutElem:: +add_rhs_elem(LocalVector& d, GridObject* elem, const MathVector vCornerCoords[]) +{ +// Only first order implementation + UG_ASSERT((TFVGeom::order == 1), "Only first order implemented."); + +// if zero data given, return + if(!m_imSourceSCV.data_given()) return; + +// get finite volume geometry + static const TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + +// loop Sub Control Volumes (SCV) + for(size_t ip = 0; ip < geo.num_scv(); ++ip) + { + // get current SCV + const typename TFVGeom::SCV& scv = geo.scv(ip); + + // get associated node + const int sh = scv.node_id(); + + // Add to local rhs + for(int d1 = 0; d1 < dim; ++d1){ + d(d1, sh) += m_imSourceSCV[ip][d1] * scv.volume() * m_imDensitySCV[ip]; + } + } +} + +template +template +inline +number +NavierStokesFV1_cutElem:: +peclet_blend(MathVector& UpwindVel, const TFVGeom& geo, size_t ip, + const MathVector& StdVel, number kinVisco) +{ + const typename TFVGeom::SCVF& scvf = geo.scvf(ip); +// compute peclet number + number Pe = VecProd(StdVel, scvf.normal())/VecTwoNormSq(scvf.normal()) + * VecDistance(geo.corners() [scvf.to()], geo.corners() [scvf.from()]) / kinVisco; + +// compute weight + const number Pe2 = Pe * Pe; + const number w = Pe2 / (5.0 + Pe2); + +// compute upwind vel + VecScaleAdd(UpwindVel, w, UpwindVel, (1.0-w), StdVel); + + return w; +} + +// computes the linearized defect w.r.t to the velocity +template +template +void NavierStokesFV1_cutElem:: +ex_velocity_grad(MathMatrix vValue[], + const MathVector vGlobIP[], + number time, int si, + const LocalVector& u, + GridObject* elem, + const MathVector vCornerCoords[], + const MathVector vLocIP[], + const size_t nip, + bool bDeriv, + std::vector > > vvvDeriv[]) +{ +// Get finite volume geometry + static const TFVGeom& geo = GeomProvider::get(m_LFEID, 1); + +// reference element + typedef typename reference_element_traits::reference_element_type ref_elem_type; + +// reference dimension + static const int refDim = ref_elem_type::dim; + +// number of shape functions + static const size_t numSH = ref_elem_type::numCorners; + +// FV1 SCVF ip + if(vLocIP == geo.scvf_local_ips()) + { + // Loop Sub Control Volume Faces (SCVF) + for (size_t ip = 0; ip < geo.num_scvf(); ++ip) + { + // Get current SCVF + const typename TFVGeom::SCVF& scvf = geo.scvf(ip); + + // Loop dimensions for direction + for(int d1 = 0; d1 < dim; ++d1) + { + // Loop dimensions for derivative + for(int d2 = 0; d2 & rTrialSpace = Provider >::get(); + + // storage for shape function at ip + MathVector vLocGrad[numSH]; + + // Reference Mapping + MathMatrix JTInv; + ReferenceMapping mapping(vCornerCoords); + + // loop ips + for(size_t ip = 0; ip < nip; ++ip) + { + // evaluate at shapes at ip + rTrialSpace.grads(vLocGrad, vLocIP[ip]); + + // Loop dimensions for direction + for(int d1 = 0; d1 < dim; ++d1) + { + // Loop dimensions for derivative + for(int d2 = 0; d2 +void NavierStokesFV1_cutElem:: +register_all_funcs(bool bHang) +{ +// switch assemble functions + if(!bHang) + { + + // register_func >(); + + register_func > >(); + } + else + { + UG_THROW("NavierStokesFV1_cutElem: Hanging Nodes not implemented.") + } +} +#endif + +#ifdef UG_DIM_2 +template<> +void NavierStokesFV1_cutElem:: +register_all_funcs(bool bHang) +{ +// switch assemble functions + if(!bHang) + { + + // register_func >(); + // register_func >(); + + register_func > >(); + // register_func > >(); + } + else + { + UG_THROW("NavierStokesFV1_cutElem: Hanging Nodes not implemented.") + } + +} + +#endif + +#ifdef UG_DIM_3 +template<> +void NavierStokesFV1_cutElem:: +register_all_funcs(bool bHang) +{ +// switch assemble functions + if(!bHang) + { + /* + register_func >(); + register_func >(); + register_func >(); + register_func >(); + */ + register_func > >(); + + } + else + { + UG_THROW("NavierStokesFV1_cutElem: Hanging Nodes not implemented.") + } +} +#endif + +template +template +void +NavierStokesFV1_cutElem:: +register_func() +{ + ReferenceObjectID id = geometry_traits::REFERENCE_OBJECT_ID; + typedef this_type T; + static const int refDim = reference_element_traits::dim; + + this->clear_add_fct(id); + this->set_prep_elem_loop_fct( id, &T::template prep_elem_loop); + this->set_prep_elem_fct( id, &T::template prep_elem); + this->set_fsh_elem_loop_fct( id, &T::template fsh_elem_loop); + this->set_add_jac_A_elem_fct( id, &T::template add_jac_A_elem); + this->set_add_jac_M_elem_fct( id, &T::template add_jac_M_elem); + this->set_add_def_A_elem_fct( id, &T::template add_def_A_elem); + this->set_add_def_M_elem_fct( id, &T::template add_def_M_elem); + this->set_add_rhs_elem_fct( id, &T::template add_rhs_elem); + + m_exVelocityGrad->template set_fct(id, this, &T::template ex_velocity_grad); + +} + +//////////////////////////////////////////////////////////////////////////////// +// explicit template instantiations +//////////////////////////////////////////////////////////////////////////////// + +#ifdef UG_DIM_2 +template class NavierStokesFV1_cutElem; +#endif +#ifdef UG_DIM_3 +template class NavierStokesFV1_cutElem; +#endif + +} // namespace NavierStokes +} // namespace ug diff --git a/incompressible/fv1/navier_stokes_fv1_cutElem.h b/incompressible/fv1/navier_stokes_fv1_cutElem.h new file mode 100644 index 0000000..97a2c91 --- /dev/null +++ b/incompressible/fv1/navier_stokes_fv1_cutElem.h @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2010-2014: G-CSC, Goethe University Frankfurt + * Author: Andreas Vogel + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + + +#ifndef __H__UG__PLUGINS__NAVIER_STOKES__INCOMPRESSIBLE__FV1__NAVIER_STOKES_FV1_CUT_ELEM_ +#define __H__UG__PLUGINS__NAVIER_STOKES__INCOMPRESSIBLE__FV1__NAVIER_STOKES_FV1_CUT_ELEM_ + +// other ug4 modules +#include "common/common.h" +#include "lib_grid/lg_base.h" + +// library intern headers +#include "lib_disc/spatial_disc/elem_disc/elem_disc_interface.h" +#include "lib_disc/spatial_disc/user_data/data_export.h" +#include "lib_disc/spatial_disc/user_data/data_import.h" + +#include "../incompressible_navier_stokes_base.h" +#include "../../upwind_interface.h" +#include "stabilization.h" + +namespace ug{ + +namespace NavierStokes{ + + +/// \ingroup lib_disc_elem_disc +/// @{ + +/// Finite Volume Element Discretization for the incompressible Navier-Stokes Equation +/** + * This class implements the IElemDisc interface to provide element local + * assemblings for the incompressible Navier-Stokes equation. + * + * The unknowns of the equation are: + *
    + *
  • \f$ \vec{u} \f$ velocity + *
  • \f$ p \f$ pressure. + *
+ * + * The equation takes the form + * \f{align*} + * \frac{\partial \rho \vec{u}}{\partial t} + * - \nabla \left( \rho \nu (\nabla \vec{u} + (\nabla \vec{u})^T) \right) + * + \nabla \cdot \left( \rho \vec{u} \vec{u}^T \right) + * + \nabla p + * &= \vec{f}\\ + * + * \nabla \cdot (\rho \vec{u}) &= 0 + * \f} + * + * with + *
    + *
  • \f$ \rho \f$ is the constant density + *
  • \f$ \nu \f$ is the kinematic viscosity (temporarily constant, implement) + *
  • \f$ \vec{f} \equiv f(\vec{x},t) \f$ is a Source Term (not implemented yet) + *
+ * + * The first equation models the conservation of momentum and is therefore + * referred to as the Momentum equation. The second equation models the + * conservation of mass and is known as the Mass continuity equation or + * simply Continuity equation. + * + * In component-wise notation, the equation reads + * + * \f{align*} + * \frac{\partial \rho u_{d_1}}{\partial t} + * - \sum_{d_2 = 1}^{\text{dim}} \frac{\partial}{\partial x_{d_2}} + * \left( \rho \nu \left( \frac{\partial u_{d_1}}{\partial x_{d_2}} + * + \frac{\partial u_{d_2}}{\partial x_{d_1}} \right)\right) + * + \sum_{d_2 = 1}^{\text{dim}} \frac{\partial}{\partial x_{d_2}} + * \left( \rho u_{d_2} u_{d_1} \right) + * + \frac{\partial p}{\partial x_{d_1}} + * &= f_{d_1} \qquad \forall \quad d_1 = 1, \dots, \text{dim}\\ + * + * \sum_{d = 1}^{\text{dim}} \frac{\partial \rho u_d}{\partial x_{d}} &= 0 + * \f} + * + * Since the density is assumed to be constant, it set to \f$\rho \equiv 1 \f$. + * + * The finite volume element discretization uses a dual mesh consisting of + * Control Volumes \f$\mathcal{B}_h\f$, that cover the domain. For each + * control volume \f$B \in \mathcal{B}_h\f$ the following equation is solved + * + * \f{align*} + * \frac{\partial}{\partial t} \int_{B} \begin{pmatrix} \vec{u} \\ 0 \end{pmatrix} dV + * + \int_{\partial B} + * \begin{pmatrix} + * - \rho \nu \left(\nabla \vec{u} + (\nabla \vec{u})^T \right) \vec{n} + * + \vec{u} \vec{u}^T \vec{n} + p \vec{n} \\ + * \vec{u} \vec{n} + * \end{pmatrix} dS + * = \int_B \begin{pmatrix} \vec{f} \\ 0 \end{pmatrix} dV + * \f} + * + * where \f$ \vec{n}\f$ is the outer normal on the boundary of the control volume + * and \f$\int_B \cdot \; dV \f$ and \f$\int_{\partial B} \cdot \; dS \f$ are the + * integration over the control volume and the integration over the boundary of + * the control volume. By \$f B_{sh} \$f is denoted the control volume associated + * to a shape function (i.e. vertices, since we use P1 Lagrange ansatz functions), + * T usually denotes the Element. + * + * In order to number the local unknowns, we use the following notation. + *
    + *
  • \f$ sh = 0, \dots, n_{sh} - 1\f$ loop the \f$n_{sh}\f$ shape functions. For + * this implementation these are the corners of the element + *
  • \f$ d = 0, \dots, \text{dim} - 1 \f$ are the velocity component of each + * corner + *
  • _P_ := dim indicates the pressure component + *
+ * The access to local unknowns of local vectors and matrices is now given by + * \f{align*} + * &\mathbf{d}(d_1, sh), \mathbf{d}(\text{\_P\_}, sh),\\ + * &\mathcal{J}(d_1, sh_1, d_2, sh_2), \mathcal{J}(d_1, sh_1, \text{\_P\_}, sh_2), + * \f} + * + * \tparam TDomain Domain + * \tparam TAlgebra Algebra + */ +template< typename TDomain> +class NavierStokesFV1_cutElem + : public IncompressibleNavierStokesBase +{ + protected: + /// Base class type + typedef IncompressibleNavierStokesBase base_type; + + /// own type + typedef NavierStokesFV1_cutElem this_type; + + public: + /// World dimension + static const int dim = base_type::dim; + + public: + /// Constructor (setting default values) + /// \{ + NavierStokesFV1_cutElem(const char* functions, const char* subsets); + NavierStokesFV1_cutElem(const std::vector& vFct, const std::vector& vSubset); + /// \} + + /// sets the kinematic viscosity + void set_kinematic_viscosity(SmartPtr > user); + + /// returns kinematic viscosity + SmartPtr > kinematic_viscosity() {return m_imKinViscosity.user_data ();} + + /// sets the density + void set_density(SmartPtr > user); + + /// returns density + SmartPtr > density() {return m_imDensitySCVF.user_data ();} + + /// sets the source function + void set_source(SmartPtr, dim> > user); + + /// returns scvf source + DataImport, dim> sourceSCVF(){ return m_imSourceSCVF;} + + /// sets the stabilization used to compute the stabilized velocities + void set_stabilization(SmartPtr > spStab) + {m_spStab = spStab;} + + /// sets stabilization based on string identifier + void set_stabilization(const std::string& name) + {m_spStab = CreateNavierStokesStabilization(name); + if(m_spConvUpwind.valid()) m_spStab->set_upwind(m_spConvUpwind);} + + /// sets stabilization and diff length method based on string identifier (only for Schneider-Raw stabilizations) + void set_stabilization(const std::string& name, const std::string& diffLength) + {SmartPtr > spStab = CreateNavierStokesStabilization(name); + spStab->set_diffusion_length(diffLength); + m_spStab = spStab; + if(m_spConvUpwind.valid()) m_spStab->set_upwind(m_spConvUpwind);} + + /// returns stabilization + SmartPtr > stabilization(){ return m_spStab;} + + /// sets a stabilization for upwinding (Physical Advection Correction) + void set_upwind(SmartPtr > spStab) + {m_spConvStab = spStab; m_spConvUpwind = SPNULL;} + + /// sets an upwinding for the convective term of momentum equation + void set_upwind(SmartPtr > spUpwind) + {m_spConvStab = SPNULL; m_spConvUpwind = spUpwind;} + + /// sets the upwind based on a string identifier + void set_upwind(const std::string& name) + {m_spConvStab = SPNULL; m_spConvUpwind = CreateNavierStokesUpwind(name); + if(m_spStab.valid() && m_spStab->upwind().invalid()) m_spStab->set_upwind(m_spConvUpwind);} + + void set_pac_upwind(bool bPac) + { + if (bPac==true){ + if (m_spConvUpwind.valid()) m_spStab->set_upwind(m_spConvUpwind); + else UG_THROW("Upwind must be specified previously.\n"); + if (m_spStab.valid()) set_upwind(m_spStab); + else UG_THROW("Stabilization must be specified previously.\n"); + } + } + + protected: + /// computes the value of the gradient of the pressure + template + void ex_velocity_grad(MathMatrix vValue[], + const MathVector vGlobIP[], + number time, int si, + const LocalVector& u, + GridObject* elem, + const MathVector vCornerCoords[], + const MathVector vLocIP[], + const size_t nip, + bool bDeriv, + std::vector > > vvvDeriv[]); + + using base_type::m_exVelocityGrad; + + public: + /// type of trial space for each function used + virtual void prepare_setting(const std::vector& vLfeID, bool bNonRegularGrid); + + /// returns string identifying disc type + virtual std::string disc_type() const {return "fv1";}; + + public: + /// prepares the element loop + /** + * This function is used to prepare the element loop. It gets called, before + * the loop over all elements starts. Here, general checks are performed: + * - Is the correct finite volume geometry used + * - Has the stabilization been specified + * + * In addition, local coordinates of evaluation points are requested by + * the DataImports in case of element-fixed points. + */ + template + void prep_elem_loop(const ReferenceObjectID roid, const int si); + + /// prepares the element for evaluation + /** + * This function prepare a specific element for assembling. This function + * is always called before any of the assemble_... functions is performed. + * Here, the Finite Volume Geometry is prepared and global positions of + * the evaluation points of the DataImports are requested (and local + * positions in case of hanging node assembling) + * + * \param[in] elem element to prepare the data for + * \param[in] u current local solution vector + * \param[in] glob_ind global indices of the local vector components + */ + template + void prep_elem(const LocalVector& u, GridObject* elem, const ReferenceObjectID roid, const MathVector vCornerCoords[]); + + /// finishes the element loop + template + void fsh_elem_loop(); + + /// adds the stiffness part to the local jacobian + /** + * This function adds the local contributions of the stiffness part to + * the local jacobian. + * + * For the definition of \f$ \vec{f}^{\text{diff}}|_{ip}\f$ and + * \f$ \vec{f}^{\text{conv}}|_{ip}\f$ see add_def_A_elem. + * + * The derivative of the diffusive flux is given by + * \f{align*} + * \frac{\partial \vec{f}^{\text{diff}}_{d_1}|_{ip}}{\partial \vec{u}_{d_2}^{sh}} + * &= - \nu \left( \delta_{d_1, d_2} \sum_{k=1}^{\text{dim}} + * \left. \frac{\partial \phi^{sh}}{\partial x_k}\right|_{ip} \vec{n_k}|_{ip} + * + \left. \frac{\partial \phi^{sh}}{\partial x_{d_1}}\right|_{ip} \vec{n_{d_2}}|_{ip} + * \right)\\ + * \frac{\partial \vec{f}^{\text{diff}}_{d_1}|_{ip}}{\partial p^{sh}} + * &= 0 + * \f} + * + * For the derivative of the convective term, we can use a fixpoint linearization + * if we only differentiate the first factor of + * \f$ + * \vec{f}^{\text{conv}}_{d_1} + * = (\vec{u}^{\text{blend}} (\vec{u}^{\text{blend}})^T)_{d_1 k} \vec{n}_k + * = \vec{u}^{\text{blend}}_{d_1} \sum_{k=1}^{\text{dim}} \vec{u}^{\text{blend}}_k \vec{n}_k + * = \vec{u}^{\text{blend}}_{d_1} \langle \vec{u}^{\text{blend}}, \vec{n} \rangle + * \f$ + * + * This gives + * \f{align*} + * \frac{\partial \vec{f}^{\text{conv}}_{d_1}}{\partial \vec{u}_{d_2}^{sh}} + * &= \langle \vec{u}^{\text{blend}}, \vec{n} \rangle + * \frac{\partial \vec{u}^{\text{blend}}_{d_1}}{\partial \vec{u}_{d_2}^{sh}} + * &= \langle \vec{u}^{\text{blend}}, \vec{n} \rangle \left( + * (1-\omega) \delta_{d_1 d_2} \phi^{sh} + * + \omega \frac{\partial \vec{u}^{\text{up}}_{d_1}}{\partial \vec{u}_{d_2}^{sh}} + * \right) + * \f} + * + * The derivative of the pressure term is given by + * \f{align*} + * \frac{\partial p \vec{n}|_{ip}}{\partial \vec{u}_{d_2}^{sh}} + * &= 0\\ + * \frac{\partial p \vec{n}|_{ip}}{\partial p^{sh}} + * &= \phi^{sh}|_{ip} \vec{n} + * \f} + * + * The derivative of the continuity term is given by + * \f{align*} + * \frac{\partial \vec{u}^{\text{stab}}|_{ip} \vec{n}|_{ip}}{\partial \vec{u}_{d_1}^{sh}} + * &= \sum_{d_2=1}^{dim} \left. \frac{\partial u_{d_2}^{\text{stab}}} + * {\partial \vec{u}_{d_1}^{sh}} \right|_{ip} \vec{n}_{d_2}\\ + * \frac{\partial \vec{u}^{\text{stab}}|_{ip} \vec{n}|_{ip}}{\partial p^{sh}} + * &=\sum_{d_2=1}^{dim} \left. \frac{\partial u_{d_2}^{\text{stab}}} + * {\partial p^{sh}} \right|_{ip} \vec{n}_{d_2}\\ + * \f} + * + * \param[in,out] J local jacobian with values added on output + * \param[in] u local vector of current solution + * \param[in] time current time step + * + * \tparam TElem Element type + * \tparam TFVGeom Finite Volume Geometry + */ + template + void add_jac_A_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]); + + /// adds the stiffness part to the local defect + /** + * This function adds the contribution of one element of the stiffness part to + * the local defect. + * + * The local solution \f$ \mathbf{u} \f$ passes the p1 conform lagrange + * dofs of the element, i.e. the unknowns in the corners of the element. + * + * Define the functional matrix of the velocity at some integration point by + * \f{align*} + * \nabla \vec{u}|_{ip} := \left(\left.\frac{\partial u_i}{\partial x_j} + * \right|_{ip}\right)_{i,j = 1,\dots,dim} + * \f} + * + * and the diffusive flux to + * \f{align*} + * \vec{f}^{\text{diff}}|_{ip} := - \nu \left( \nabla \vec{u}|_{ip} + + * (\nabla \vec{u}|_{ip})^T) \right) \cdot \vec{n}_{ip} + * \f} + * + * Suppose that a procedure producing some upwind velocity \f$ \vec{u}^{up}\f$ + * is available for each ip. Then, we define the convective flux to + * \f{align*} + * \vec{f}^{\text{conv}}|_{ip} := \left( \vec{u}^{up}|_{ip} \vec{u}^{up}|_{ip}^T + * \right) \cdot \vec{n}_{ip} + * \f} + * + * Define the pressure at the ip to be interpolated as + * \f{align*} + * p|_{ip} := \sum_{sh=0}^{n_{sh}-1} \mathbf{u}(\text{\_P\_}, sh) \phi_{sh} + * \f} + * + * The contribution added for the momentum equation is: + * \f{align*} + * \forall d=0,\dots, \text{dim}-1, \quad + * \forall sh=0,\dots, n_{sh}-1: \qquad + * \mathbf{d}(d, sh) &\text{ +=} + * \int_{\partial B_{sh} \cap T} \vec{f}^{\text{diff}}_d + * + \vec{f}^{\text{conv}}_d + * + p \vec{n}_d \;dS + * + * \approx \sum_{i=1}^{n_{\partial B_{sh}}} + * |\partial B^i_{sh} \cap T| + * \left( \left.\vec{f}^{\text{diff}}_d\right|_{ip} + * + \left.\vec{f}^{\text{conv}}_d\right|_{ip} + * + p|_{ip} \vec{n}|_{ip} \right)\\ + * \f} + * + * Assume, that some stabilized velocity \f$ \vec{u}^{stab}\f$ is available + * for each ip. + * Then, the contribution added for the continuity equation is: + * \f{align*} + * \forall sh=0,\dots, n_{sh}-1: \qquad + * \mathbf{d}(\text{\_P\_}, sh) &\text{ +=} + * \int_{\partial B_{sh} \cap T} \vec{u}^{stab} \cdot \vec{n} \;dS + * + * \approx \sum_{i=1}^{n_{\partial B_{sh}}} + * |\partial B^i_{sh} \cap T| \; \vec{u}^{stab}|_{ip} \cdot \vec{n}|_{ip}\\ + * \f} + * + * \param[in,out] d local defect with values added on output + * \param[in] u local vector of current solution + * \param[in] time current time step + * + * \tparam TElem Element type + * \tparam TFVGeom Finite Volume Geometry + */ + template + void add_def_A_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]); + + /// adds the mass part to the local jacobian + /** + * This function adds the contribution of one element of the mass part to + * the local jacobian. + * + * The local solution \f$ \mathbf{u} \f$ passes the p1 conform lagrange + * dofs of the element, i.e. the unknowns in the corners of the element. + * + * The contribution added is: + * \f{align*} + * \forall d=0,\dots, \text{dim}-1, \quad + * \forall sh=0,\dots, n_{sh}-1: \qquad + * \mathcal{J}(d, sh, d, sh) &\text{ +=} \int_{B_{sh} \cap T} dV + * = |B_{sh} \cap T|\\ + * \f} + * + * \param[in,out] J local jacobian with values added on output + * \param[in] u local vector of current solution + * \param[in] time current time step + * + * \tparam TElem Element type + * \tparam TFVGeom Finite Volume Geometry + */ + template + void add_jac_M_elem(LocalMatrix& J, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]); + + /// adds the mass part to the local defect + /** + * This function adds the contribution of one element of the mass part to + * the local defect. + * + * The local solution \f$ \mathbf{u} \f$ passes the p1 conform lagrange + * dofs of the element, i.e. the unknowns in the corners of the element. + * + * The contribution added is: + * \f{align*} + * \forall d=0,\dots, \text{dim}-1, \quad + * \forall sh=0,\dots, n_{sh}-1: \qquad + * \mathbf{d}(d, sh) &\text{ +=} \int_{B_{sh} \cap T} \vec{u}_d dV + * \approx |B_{sh} \cap T| \mathbf{u}(d, sh)\\ + * + * \mathbf{d}(\text{\_P\_, sh}) &\text{ +=} 0 + * \f} + * + * \param[in,out] d local defect with values added on output + * \param[in] u local vector of current solution + * \param[in] time current time step + * + * \tparam TElem Element type + * \tparam TFVGeom Finite Volume Geometry + */ + template + void add_def_M_elem(LocalVector& d, const LocalVector& u, GridObject* elem, const MathVector vCornerCoords[]); + + /// adds the source part to the local defect + /** + * This function adds the contribution of one element of the source part to + * the local defect, if a source function \f$ \vec{f} \f$ has been specified. + * Otherwise a zero source function is assumed. + * + * The contribution added is: + * \f{align*} + * \forall d=0,\dots, \text{dim}-1, \quad + * \forall sh=0,\dots, n_{sh}-1: \qquad + * \mathbf{d}(d, sh) &\text{ +=} \int_{B_{sh} \cap T} \vec{f}_d \; dV + * \approx |B_{sh} \cap T| \; \vec{f}_d|_{ip} + * \f} + * + * \param[in,out] d local defect with values added on output + * \param[in] time current time step + * + * \tparam TElem Element type + * \tparam TFVGeom Finite Volume Geometry + */ + template + void add_rhs_elem(LocalVector& d, GridObject* elem, const MathVector vCornerCoords[]); + + /// computes the pecled blended Upwind veloctity + /** + * This function compute the peclet blended velocity. First, the + * standard interpolated (no upwind) velocity is computed at the integration + * point of the scvf: + * \f{align*} + * \vec{u}^{\text{std}}_d|_{ip} + * := \sum_{sh=0}^{n_{sh}-1} \mathbf{u}(d, sh) \; \phi_{sh} + * \f} + * + * Defining the Peclet Number + * \f{align*} + * Pe := \frac{\vec{u}^{\text{std}}|_{ip} \cdot \vec{n}|_{ip} \; L}{\nu} + * \f} + * with the Viscosity \f$ \nu \f$ and the diffusion length \f$ L \f$, a + * weighting factor is computed: + * \f{align*} + * \omega := \frac{Pe^2}{5 + Pe^2} + * \f} + * + * and the blended velocity is computed to + * \f{align*} + * \vec{u}^{\text{blend}}|_{ip} := \omega \vec{u}^{\text{up}} + * + (1-\omega) \vec{u}^{\text{std}} + * \f} + * + * \param[in,out] UpwindVel upwind vel on entry, blended vel on exit + * \param[in] scvf SubControlVolumeFace of the Velocity + * \param[in] StdVel standard interpolation vel + * \param[in] kinVisco kinematic Viscosity at scvf + * \return \f$\omega\f$ weighting factor + */ + template + inline number peclet_blend(MathVector& UpwindVel, const TFVGeom& geo, size_t ip, + const MathVector& StdVel, number kinVisco); + + protected: + /// Data import for source + DataImport, dim> m_imSourceSCV; + DataImport, dim> m_imSourceSCVF; + + /// Data import for kinematic viscosity + DataImport m_imKinViscosity; + + /// Data import for density + DataImport m_imDensitySCVF; + DataImport m_imDensitySCV; + + /// Stabilization for velocity in continuity equation + SmartPtr > m_spStab; + + /// Stabilization for velocity in convective term of momentum equation + /// Here, the stabilization is used as an upwinding + SmartPtr > m_spConvStab; + + /// Upwinding for velocity in convective term of momentum equation + SmartPtr > m_spConvUpwind; + + /// abbreviation for pressure + static const size_t _P_ = dim; + + using base_type::m_bPecletBlend; + using base_type::m_bFullNewtonFactor; + using base_type::m_bStokes; + using base_type::m_bLaplace; + + virtual void init(); + + protected: + /// current shape function set (needed for GeomProvider::get()) + LFEID m_LFEID; + + /// flag for moving particle discretisation + bool m_bCutElemGeom; + + /// register utils + /// \{ + virtual void register_all_funcs(bool bHang); + template void register_func(); + /// \} +}; + +/// @} + +} // namespace NavierStokes +} // end namespace ug + +#endif /*__H__UG__PLUGINS__NAVIER_STOKES__INCOMPRESSIBLE__FV1__NAVIER_STOKES_FV1_CUT_ELEM_*/ diff --git a/incompressible/fv1/register_fv1.cpp b/incompressible/fv1/register_fv1.cpp index f800258..0787e09 100644 --- a/incompressible/fv1/register_fv1.cpp +++ b/incompressible/fv1/register_fv1.cpp @@ -35,6 +35,9 @@ #include "bridge/util_domain_algebra_dependent.h" #include "navier_stokes_fv1.h" +#include "navier_stokes_fv1_cutElem.h" +#include "bnd/inflow_fv1_cutElem.h" + #include "bnd/inflow_fv1.h" #include "bnd/no_normal_stress_outflow_fv1.h" #include "bnd/symmetric_boundary_fv1.h" @@ -85,6 +88,17 @@ static void DomainAlgebra(Registry& reg, string grp) reg.add_class_to_group(name, "NavierStokesInflowFV1", tag); } + // NavierStokesInflow FV1 cutElem + { + typedef NavierStokesInflowFV1_cutElem T; + typedef NavierStokesInflowBase TBase; + string name = string("NavierStokesInflowFV1_cutElem").append(suffix); + reg.add_class_(name, grp) + .template add_constructor >)>("MasterElemDisc") + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "NavierStokesInflowFV1_cutElem", tag); + } + typedef ug::GridFunction TFct; static const int dim = TDomain::dim; @@ -163,12 +177,32 @@ static void Domain(Registry& reg, string grp) static const int dim = TDomain::dim; string suffix = GetDomainSuffix(); string tag = GetDomainTag(); - - // Navier-Stokes FV1 + + // Navier-Stokes FV1 + { + typedef NavierStokesFV1 T; + typedef IncompressibleNavierStokesBase TBase; + string name = string("NavierStokesFV1").append(suffix); + reg.add_class_(name, grp) + .template add_constructor("Functions#Subset(s)") + .template add_constructor&, const std::vector&)>("Functions#Subset(s)") + .add_method("set_stabilization", static_cast >)>(&T::set_stabilization)) + .add_method("set_stabilization", static_cast(&T::set_stabilization)) + .add_method("set_stabilization", static_cast(&T::set_stabilization)) + .add_method("set_upwind", static_cast >)>(&T::set_upwind)) + .add_method("set_upwind", static_cast >)>(&T::set_upwind)) + .add_method("set_upwind", static_cast(&T::set_upwind)) + .add_method("set_pac_upwind", &T::set_pac_upwind, "", "Set pac upwind") + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "NavierStokesFV1", tag); + } + + + // Navier-Stokes FV1_cutElem { - typedef NavierStokesFV1 T; + typedef NavierStokesFV1_cutElem T; typedef IncompressibleNavierStokesBase TBase; - string name = string("NavierStokesFV1").append(suffix); + string name = string("NavierStokesFV1_cutElem").append(suffix); reg.add_class_(name, grp) .template add_constructor("Functions#Subset(s)") .template add_constructor&, const std::vector&)>("Functions#Subset(s)") @@ -179,8 +213,8 @@ static void Domain(Registry& reg, string grp) .add_method("set_upwind", static_cast >)>(&T::set_upwind)) .add_method("set_upwind", static_cast(&T::set_upwind)) .add_method("set_pac_upwind", &T::set_pac_upwind, "", "Set pac upwind") - .set_construct_as_smart_pointer(true); - reg.add_class_to_group(name, "NavierStokesFV1", tag); + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "NavierStokesFV1_cutElem", tag); } diff --git a/incompressible/fv1/stabilization.cpp b/incompressible/fv1/stabilization.cpp index 7ddd0e2..8499bbe 100644 --- a/incompressible/fv1/stabilization.cpp +++ b/incompressible/fv1/stabilization.cpp @@ -124,6 +124,291 @@ template void NavierStokesFIELDSStabilization:: update(const FV1Geometry* geo, + const LocalVector& vCornerValue, + const MathVector vStdVel[], + const bool bStokes, + const DataImport& kinVisco, + const DataImport& density, + const DataImport, dim>* pSource, + const LocalVector* pvCornerValueOldTime, number dt) +{ + // abbreviation for pressure + static const size_t _P_ = dim; + + // Some constants + static const size_t numIp = FV1Geometry::numSCVF; + static const size_t numSh = FV1Geometry::numSCV; + + // compute upwind (no convective terms for the Stokes eq. => no upwind) + if (! bStokes) this->compute_upwind(geo, vStdVel); + + // compute diffusion length + this->compute_diff_length(*geo); + + // cache values + number vViscoPerDiffLenSq[numIp]; + for(size_t ip = 0; ip < numIp; ++ip) + vViscoPerDiffLenSq[ip] = kinVisco[ip] * diff_length_sq_inv(ip); + + number vNormStdVelPerConvLen[numIp]; + if(!bStokes) + for(size_t ip = 0; ip < numIp; ++ip) + vNormStdVelPerConvLen[ip] = VecTwoNorm(vStdVel[ip]) / upwind_conv_length(ip); + + // Find out if upwinded velocities depend on other ip velocities. In that case + // we have to solve a matrix system. Else the system is diagonal and we can + // compute the inverse directly + + // diagonal case (i.e. upwind vel depend only on corner vel or no upwind) + if(bStokes || !non_zero_shape_ip()) + { + // We can solve the systems ip by ip + for(size_t ip = 0; ip < numIp; ++ip) + { + // get SubControlVolumeFace + const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + + // First, we compute the contributions to the diagonal + // Note: - There is no contribution of the upwind vel to the diagonal + // in this case, only for non-diag problems + // - The diag does not depend on the dimension + + // Diffusion part + number diag = vViscoPerDiffLenSq[ip]; + + // Time part + if(pvCornerValueOldTime != NULL) + diag += 1./dt; + + // Convective Term (no convective terms in the Stokes eq.) + if (! bStokes) + diag += vNormStdVelPerConvLen[ip]; + + // Loop components of velocity + for(size_t d = 0; d < (size_t)dim; d++) + { + // Now, we can assemble the rhs. This rhs is assembled by all + // terms, that are non-dependent on the ip vel. + // Note, that we can compute the stab_shapes on the fly when setting + // up the system. + + // Source + number rhs = 0.0; + if(pSource != NULL) + rhs = (*pSource)[ip][d]; + + // Time + if(pvCornerValueOldTime != NULL) + { + // interpolate old time step + number oldIPVel = 0.0; + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + oldIPVel += scvf.shape(sh) * (*pvCornerValueOldTime)(d, sh); + + // add to rhs + rhs += oldIPVel / dt; + } + + // loop shape functions + for(size_t k = 0; k < scvf.num_sh(); ++k) + { + // Diffusion part + number sumVel = vViscoPerDiffLenSq[ip] * scvf.shape(k); + + // Convective term + if (! bStokes) // no convective terms in the Stokes eq. + sumVel += vNormStdVelPerConvLen[ip] * upwind_shape_sh(ip, k); + + // Add to rhs + rhs += sumVel * vCornerValue(d, k); + + // set stab shape + stab_shape_vel(ip, d, d, k) = sumVel / diag; + + // Pressure part + const number sumP = -1.0 * (scvf.global_grad(k))[d] / density[ip]; + + // Add to rhs + rhs += sumP * vCornerValue(_P_, k); + + // set stab shape + stab_shape_p(ip, d, k) = sumP / diag; + } + + // Finally, the can invert this row + stab_vel(ip)[d] = rhs / diag; + } + } + } + /// need to solve system + else + { + // For the FIELDS stabilization, there is no connection between the + // velocity components. Thus we can solve a system of size=numIP for + // each component of the velocity separately. This results in smaller + // matrices, that we have to invert. + + // First, we have to assemble the Matrix, that includes all connections + // between the ip velocity component. Note, that in this case the + // matrix is non-diagonal and we must invert it. + // The Matrix is the same for all dim-components, thus we assemble it only + // once + + // size of the system + static const size_t N = numIp; + + // a fixed size matrix + DenseMatrix< FixedArray2 > mat; + + // reset all values of the matrix to zero + mat = 0.0; + + // Loop integration points + for(size_t ip = 0; ip < numIp; ++ip) + { + // Time part + if(pvCornerValueOldTime != NULL) + mat(ip, ip) += 1./dt; + + // Diffusion part + mat(ip, ip) += vViscoPerDiffLenSq[ip]; + + // cache this value + const number scale = vNormStdVelPerConvLen[ip]; + + // Convective Term (standard) + mat(ip, ip) += scale; + + // Convective Term by upwind + for(size_t ip2 = 0; ip2 < numIp; ++ip2) + mat(ip, ip2) -= upwind_shape_ip(ip, ip2) * scale; + } + + // we now create a matrix, where we store the inverse matrix + typename block_traits > >::inverse_type inv; + + // get the inverse + if(!GetInverse(inv, mat)) + UG_THROW("Could not compute inverse."); + + // loop dimensions (i.e. components of the velocity) + for(int d = 0; d < dim; ++d) + { + // create two vectors + DenseVector< FixedArray1 > contVel[numSh]; + DenseVector< FixedArray1 > contP[numSh]; + + // Now, we can create several vector that describes the contribution of the + // corner velocities and the corner pressure. For each of this contribution + // components, we will apply the inverted matrix to get the stab_shapes + + // Loop integration points + for(size_t ip = 0; ip < numIp; ++ip) + { + // get SubControlVolumeFace + const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + + // loop shape functions + for(size_t k = 0; k < numSh; ++k) + { + // Diffusion part + contVel[k][ip] = vViscoPerDiffLenSq[ip] * scvf.shape(k); + + // Convection part + contVel[k][ip] += vNormStdVelPerConvLen[ip] * upwind_shape_sh(ip, k); + + // Pressure part + contP[k][ip] = -1.0 * (scvf.global_grad(k))[d] / density[ip]; + } + } + + // solution vector + DenseVector< FixedArray1 > xVel, xP; + + // compute all stab_shapes + for(size_t k = 0; k < numSh; ++k) + { + // apply for vel stab_shape + MatMult(xVel, 1.0, inv, contVel[k]); + + // apply for pressure stab_shape + MatMult(xP, 1.0, inv, contP[k]); + + // write values in data structure + //\todo: can we optimize this, e.g. without copy? + for(size_t ip = 0; ip < numIp; ++ip) + { + // write stab_shape for vel + stab_shape_vel(ip, d, d, k) = xVel[ip]; + + // write stab_shape for pressure + stab_shape_p(ip, d, k) = xP[ip]; + } + } + + // Finally, we can compute the values of the stabilized velocity for each + // integration point + + // vector of all contributions + DenseVector< FixedArray1 > f; + + // Loop integration points + for(size_t ip = 0; ip < numIp; ++ip) + { + // get SubControlVolumeFace + const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + + // Source + f[ip] = 0.0; + if(pSource != NULL) + f[ip] = (*pSource)[ip][d]; + + // Time + if(pvCornerValueOldTime != NULL) + { + // interpolate old time step + // \todo: Is this ok? Or do we need the old stabilized vel ? + number oldIPVel = 0.0; + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + oldIPVel += scvf.shape(sh) * (*pvCornerValueOldTime)(d, sh); + + // add to rhs + f[ip] += oldIPVel / dt; + } + } + + // sum up all contributions of vel and p for rhs. + for(size_t k = 0; k < numSh; ++k) + { + // add velocity contribution + VecScaleAdd(f, 1.0, f, vCornerValue(d, k), contVel[k]); + + // add pressure contribution + VecScaleAdd(f, 1.0, f, vCornerValue(_P_, k), contP[k]); + } + + // invert the system for all contributions + DenseVector< FixedArray1 > x; + MatMult(x, 1.0, inv, f); + + // write values in data structure + //\todo: can we optimize this, e.g. without copy? + for(size_t ip = 0; ip < numIp; ++ip) + { + // write stab_shape for vel + stab_vel(ip)[d] = x[ip]; + } + } // end dim loop + + } // end switch for non-diag +} + + +template +template +void +NavierStokesFIELDSStabilization:: +update(const DimFV1FTGeometry >* geo, const LocalVector& vCornerValue, const MathVector vStdVel[], const bool bStokes, @@ -132,13 +417,25 @@ update(const FV1Geometry* geo, const DataImport, dim>* pSource, const LocalVector* pvCornerValueOldTime, number dt) { + if ( geo->num_scvf() == 0 ) + return; + // abbreviation for pressure static const size_t _P_ = dim; + typedef DimFV1FTGeometry > TFVGeom; + +// hard coded for mat = in else-case! If not, assembling of mat WRONG! + static const size_t maxNumIp = dim+1; //TFVGeom::maxNumSCVF; + static const size_t maxNumSh = dim+1; //TFVGeom::maxNSH; + // Some constants - static const size_t numIp = FV1Geometry::numSCVF; - static const size_t numSh = FV1Geometry::numSCV; + //static const size_t numIp = TFVGeom::numSCVF; + //static const size_t numSh = TFVGeom::numSCV; + const size_t numIp = geo->num_scvf(); + const size_t numSh = geo->num_scv(); + // compute upwind (no convective terms for the Stokes eq. => no upwind) if (! bStokes) this->compute_upwind(geo, vStdVel); @@ -166,7 +463,7 @@ update(const FV1Geometry* geo, for(size_t ip = 0; ip < numIp; ++ip) { // get SubControlVolumeFace - const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); // First, we compute the contributions to the diagonal // Note: - There is no contribution of the upwind vel to the diagonal @@ -243,6 +540,8 @@ update(const FV1Geometry* geo, /// need to solve system else { + UG_THROW("in stabilisation.h: FIELDS::update(): Stop! this case is not implemented for DimFV1FTGeometry!\n"); + // For the FIELDS stabilization, there is no connection between the // velocity components. Thus we can solve a system of size=numIP for // each component of the velocity separately. This results in smaller @@ -255,7 +554,7 @@ update(const FV1Geometry* geo, // once // size of the system - static const size_t N = numIp; + static const size_t N = maxNumIp; //numIp; // a fixed size matrix DenseMatrix< FixedArray2 > mat; @@ -295,8 +594,8 @@ update(const FV1Geometry* geo, for(int d = 0; d < dim; ++d) { // create two vectors - DenseVector< FixedArray1 > contVel[numSh]; - DenseVector< FixedArray1 > contP[numSh]; + DenseVector< FixedArray1 > contVel[maxNumSh]; + DenseVector< FixedArray1 > contP[maxNumSh]; // Now, we can create several vector that describes the contribution of the // corner velocities and the corner pressure. For each of this contribution @@ -306,7 +605,7 @@ update(const FV1Geometry* geo, for(size_t ip = 0; ip < numIp; ++ip) { // get SubControlVolumeFace - const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); // loop shape functions for(size_t k = 0; k < numSh; ++k) @@ -356,7 +655,7 @@ update(const FV1Geometry* geo, for(size_t ip = 0; ip < numIp; ++ip) { // get SubControlVolumeFace - const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); // Source f[ip] = 0.0; @@ -771,6 +1070,354 @@ update(const FV1Geometry* geo, } // end switch for non-diag } +template +template +void +NavierStokesFLOWStabilization:: +update(const DimFV1FTGeometry >* geo, + const LocalVector& vCornerValue, + const MathVector vStdVel[], + const bool bStokes, + const DataImport& kinVisco, + const DataImport& density, + const DataImport, dim>* pSource, + const LocalVector* pvCornerValueOldTime, number dt) +{ + // abbreviation for pressure + static const size_t _P_ = dim; + + typedef DimFV1FTGeometry > TFVGeom; + + // hard coded for mat = in else-case! If not, assembling of mat WRONG! + static const size_t maxNumIp = dim+2; //TFVGeom::maxNumSCVF; + static const size_t maxNumSh = dim+2; //TFVGeom::maxNSH; + + // Some constants + //static const size_t numIp = TFVGeom::numSCVF; + //static const size_t numSh = TFVGeom::numSCV; + const size_t numIp = geo->num_scvf(); + const size_t numSh = geo->num_scv(); + + // compute upwind and downwind (no convective terms for the Stokes eq. => no upwind) + if (! bStokes) + { + this->compute_upwind(geo, vStdVel); + this->compute_downwind(geo, vStdVel); + } + + // compute diffusion length + this->compute_diff_length(*geo); + + // cache values + number vViscoPerDiffLenSq[numIp]; + for(size_t ip = 0; ip < numIp; ++ip) + vViscoPerDiffLenSq[ip] = kinVisco[ip] * diff_length_sq_inv(ip); + + number vNormStdVelPerConvLen[numIp]; + number vNormStdVelPerDownLen[numIp]; + if(!bStokes) + for(size_t ip = 0; ip < numIp; ++ip) + { + const number norm = VecTwoNorm(vStdVel[ip]); + vNormStdVelPerConvLen[ip] = norm / upwind_conv_length(ip); + vNormStdVelPerDownLen[ip] = norm / (downwind_conv_length(ip) + upwind_conv_length(ip)); + } + + // Find out if upwinded velocities depend on other ip velocities. In that case + // we have to solve a matrix system. Else the system is diagonal and we can + // compute the inverse directly + + // diagonal case (i.e. upwind vel depend only on corner vel or no upwind) + if(bStokes || !non_zero_shape_ip()) + { + // Loop integration points + for(size_t ip = 0; ip < numIp; ++ip) + { + // get SubControlVolumeFace + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + + // First, we compute the contributions to the diagonal + // Note: - There is no contribution of the upwind vel to the diagonal + // in this case, only for non-diag problems + // - The diag does not depend on the dimension + + // the diagonal entry + number diag = vViscoPerDiffLenSq[ip]; + + // Time part + if(pvCornerValueOldTime != NULL) + diag += 1./dt; + + // Convective Term (no convective terms in the Stokes eq.) + if (! bStokes) + diag += vNormStdVelPerConvLen[ip]; + + // Loop components of velocity + for(int d = 0; d < dim; d++) + { + // Now, we can assemble the rhs. This rhs is assembled by all + // terms, that are non-dependent on the ip vel. + // Note, that we can compute the stab_shapes on the fly when setting + // up the system. + + // Source + number rhs = 0.0; + if(pSource != NULL) + rhs = (*pSource)[ip][d]; + + // Time + if(pvCornerValueOldTime != NULL) + { + // interpolate old time step + number oldIPVel = 0.0; + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + oldIPVel += scvf.shape(sh) * (*pvCornerValueOldTime)(d, sh); + + // add to rhs + rhs += oldIPVel / dt; + } + + // loop shape functions + for(size_t k = 0; k < scvf.num_sh(); ++k) + { + // Diffusion part + number sumVel = vViscoPerDiffLenSq[ip] * scvf.shape(k); + + // Convective term (no convective terms in the Stokes eq.) + if (! bStokes) + { + sumVel += vNormStdVelPerConvLen[ip] * upwind_shape_sh(ip, k); + + sumVel += vNormStdVelPerDownLen[ip] * + (downwind_shape_sh(ip, k) - upwind_shape_sh(ip, k)); + } + + for(int d2 = 0; d2 < dim; ++d2) + { + if(d2 == d) continue; + + sumVel -= vStdVel[ip][d2] * (scvf.global_grad(k))[d2]; + } + + // Add to rhs + rhs += sumVel * vCornerValue(d, k); + + // set stab shape + stab_shape_vel(ip, d, d, k) = sumVel / diag; + + for(int d2 = 0; d2 < dim; ++d2) + { + if(d2 == d) continue; + + const number sumVel2 = vStdVel[ip][d] * (scvf.global_grad(k))[d2]; + + rhs += sumVel2 * vCornerValue(d2, k); + + stab_shape_vel(ip, d, d2, k) = sumVel2 / diag; + } + + // Pressure part + const number sumP = -1.0 * (scvf.global_grad(k))[d] / density[ip]; + + // Add to rhs + rhs += sumP * vCornerValue(_P_, k); + + // set stab shape + stab_shape_p(ip, d, k) = sumP / diag; + } + + // Finally, the can invert this row + stab_vel(ip)[d] = rhs / diag; + } + } + } + /// need to solve system + else + { + UG_THROW("in stabilisation.h: FLOW::update(): Stop! this case is not implemented for DimFV1FTGeometry!\n"); + + // For the FLOW stabilization, there is no connection between the + // velocity components. Thus we can solve a system of size=numIP for + // each component of the velocity separately. This results in smaller + // matrices, that we have to invert. + + // First, we have to assemble the Matrix, that includes all connections + // between the ip velocity component. Note, that in this case the + // matrix is non-diagonal and we must invert it. + // The Matrix is the same for all dim-components. Thus we invert it only + // once. + + // size of the system + static const size_t N = maxNumIp; + + // a fixed size matrix + DenseMatrix< FixedArray2 > mat; + + // reset all values of the matrix to zero + mat = 0.0; + + // Loop integration points + for(size_t ip = 0; ip < numIp; ++ip) + { + // Time part + if(pvCornerValueOldTime != NULL) + mat(ip, ip) += 1./dt; + + // Diffusion part + mat(ip, ip) += vViscoPerDiffLenSq[ip]; + + // Convective Term (standard) + mat(ip, ip) += vNormStdVelPerConvLen[ip]; + + for(size_t ip2 = 0; ip2 < numIp; ++ip2) + { + // Convective Term by upwind + mat(ip, ip2) -= upwind_shape_ip(ip, ip2) * vNormStdVelPerConvLen[ip]; + + // correction of divergence error + mat(ip,ip2) += vNormStdVelPerDownLen[ip] * + (upwind_shape_ip(ip, ip2) - downwind_shape_ip(ip, ip2)); + } + } + + // we now create a matrix, where we store the inverse matrix + typename block_traits > >::inverse_type inv; + + // get the inverse + if(!GetInverse(inv, mat)) + UG_THROW("Could not compute inverse."); + + + // create vectors + DenseVector< FixedArray1 > contVel[dim][maxNumSh]; + DenseVector< FixedArray1 > contP[maxNumSh]; + DenseVector< FixedArray1 > xP; + DenseVector< FixedArray1 > xVel; + + // Now, we can create several vector that describes the contribution of the + // corner velocities. For each of this contribution + // components, we will apply the inverted matrix to get the stab_shapes + + // Loop integration points + for(int d = 0; d < dim; ++d) + { + for(size_t ip = 0; ip < numIp; ++ip) + { + // get SubControlVolumeFace + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + + // loop shape functions + for(size_t k = 0; k < numSh; ++k) + { + // Pressure part + contP[k][ip] = -1.0 * (scvf.global_grad(k))[d] / density[ip]; + + // Diffusion part + contVel[d][k][ip] = vViscoPerDiffLenSq[ip] * scvf.shape(k); + + // Convection part + contVel[d][k][ip] += vNormStdVelPerConvLen[ip] * upwind_shape_sh(ip, k); + + // terms for correction of divergence error + contVel[d][k][ip] += vNormStdVelPerDownLen[ip] * + (downwind_shape_sh(ip, k) - upwind_shape_sh(ip, k)); + + + for(int d2 = 0; d2 < dim; ++d2) + { + if(d2 == d) continue; + + contVel[d][k][ip] -= vStdVel[ip][d2] + * (scvf.global_grad(k))[d2]; + + contVel[d2][k][ip] = vStdVel[ip][d] + * (scvf.global_grad(k))[d2]; + } + } + } // end ip + + // compute all stab_shapes + for(size_t k = 0; k < numSh; ++k) + { + // apply for pressure stab_shape + MatMult(xP, 1.0, inv, contP[k]); + + // write stab_shape for pressure + //\todo: can we optimize this, e.g. without copy? + for(size_t ip = 0; ip < numIp; ++ip) + stab_shape_p(ip, d, k) = xP[ip]; + + // compute vel stab_shapes + for(int d2 = 0; d2 < dim; ++d2) + { + // apply for vel stab_shape + MatMult(xVel, 1.0, inv, contVel[d2][k]); + + // write stab_shape for vel + //\todo: can we optimize this, e.g. without copy? + for(size_t ip = 0; ip < numIp; ++ip) + stab_shape_vel(ip, d, d2, k) = xVel[ip]; + } + } + + // Finally, we can compute the values of the stabilized velocity for each + // integration point + + // vector of all contributions + DenseVector< FixedArray1 > f; + + // sum up all contributions of vel and p for rhs. + f = 0.0; + for(size_t k = 0; k < numSh; ++k) + { + // add velocity contribution + for(int d2 = 0; d2 < dim; ++d2) + VecScaleAdd(f, 1.0, f, vCornerValue(d2, k), contVel[d2][k]); + + // add pressure contribution + VecScaleAdd(f, 1.0, f, vCornerValue(_P_, k), contP[k]); + } + + // Loop integration points + for(size_t ip = 0; ip < numIp; ++ip) + { + // get SubControlVolumeFace + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + + // Source + if(pSource != NULL) + f[ip] += (*pSource)[ip][d]; + + // Time + if(pvCornerValueOldTime != NULL) + { + // interpolate old time step + // \todo: Is this ok? Or do we need the old stabilized vel ? + number oldIPVel = 0.0; + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + oldIPVel += scvf.shape(sh) * (*pvCornerValueOldTime)(d, sh); + + // add to rhs + f[ip] += oldIPVel / dt; + } + } + + // invert the system for all contributions + DenseVector< FixedArray1 > x; + MatMult(x, 1.0, inv, f); + + // write values in data structure + //\todo: can we optimize this, e.g. without copy? + for(size_t ip = 0; ip < numIp; ++ip) + { + // write stab_shape for vel + stab_vel(ip)[d] = x[ip]; + } + } // end dim + + } // end switch for non-diag +} + template <> void NavierStokesFLOWStabilization<1>::register_func() diff --git a/incompressible/fv1/stabilization.h b/incompressible/fv1/stabilization.h index 2086600..266fd00 100644 --- a/incompressible/fv1/stabilization.h +++ b/incompressible/fv1/stabilization.h @@ -39,6 +39,7 @@ #include "../../upwind_interface.h" #include "lib_disc/spatial_disc/disc_util/fv1_geom.h" +#include "lib_disc/spatial_disc/disc_util/fv1FT_geom.h" #include "lib_disc/spatial_disc/user_data/data_import.h" @@ -111,11 +112,21 @@ class INavierStokesFV1Stabilization UG_THROW("No update function registered for Geometry "<::get(); - m_numScvf = geo.num_scvf(); - m_numSh = geo.num_sh(); + + // set sizes + if (TFVGeom::staticLocalData) + { + TFVGeom& geo = GeomProvider::get(); + m_numScvf = geo.num_scvf(); + m_numSh = geo.num_sh(); + } + else + { + // hard code as !maxNum, since maybe crash, e.g. in update() for computation of mat!! + m_numScvf = traits::maxNumSCVF; + m_numSh = traits::maxNSH; + } + // set sizes in upwind if(m_spUpwind.valid()) m_spUpwind->template set_geometry_type(); } @@ -332,7 +343,7 @@ class INavierStokesSRFV1Stabilization /// diff length number diff_length_sq_inv(size_t scvf) const { - UG_NSSTAB_ASSERT(scvf < this_type::num_ip(), "Invalid index."); + //UG_NSSTAB_ASSERT(scvf < this_type::num_ip(), "Invalid index."); return m_vDiffLengthSqInv[scvf]; } @@ -476,12 +487,24 @@ class NavierStokesFIELDSStabilization const DataImport, dim>* pSource, const LocalVector* pvCornerValueOldTime, number dt); + /// update of values for DimFV1FTGeometry + template + void update(const DimFV1FTGeometry >* geo, + const LocalVector& vCornerValue, + const MathVector vStdVel[], + const bool bStokes, + const DataImport& kinVisco, + const DataImport& density, + const DataImport, dim>* pSource, + const LocalVector* pvCornerValueOldTime, number dt); + private: void register_func(); template void register_func() { + { typedef FV1Geometry TGeom; typedef void (this_type::*TFunc)(const TGeom* geo, const LocalVector& vCornerValue, @@ -492,7 +515,23 @@ class NavierStokesFIELDSStabilization const DataImport, dim>* pSource, const LocalVector* pvCornerValueOldTime, number dt); + + this->template register_update_func(&this_type::template update); + } + { + typedef DimFV1FTGeometry > TGeom; + typedef void (this_type::*TFunc)(const TGeom* geo, + const LocalVector& vCornerValue, + const MathVector vStdVel[], + const bool bStokes, + const DataImport& kinVisco, + const DataImport& density, + const DataImport, dim>* pSource, + const LocalVector* pvCornerValueOldTime, number dt); + + this->template register_update_func(&this_type::template update); + } } }; @@ -557,13 +596,25 @@ class NavierStokesFLOWStabilization const DataImport& density, const DataImport, dim>* pSource, const LocalVector* pvCornerValueOldTime, number dt); - + + /// update of values for DimFV1FTGeometry + template + void update(const DimFV1FTGeometry >* geo, + const LocalVector& vCornerValue, + const MathVector vStdVel[], + const bool bStokes, + const DataImport& kinVisco, + const DataImport& density, + const DataImport, dim>* pSource, + const LocalVector* pvCornerValueOldTime, number dt); + private: void register_func(); template void register_func() { + { typedef FV1Geometry TGeom; typedef void (this_type::*TFunc)(const TGeom* geo, const LocalVector& vCornerValue, @@ -573,8 +624,22 @@ class NavierStokesFLOWStabilization const DataImport& density, const DataImport, dim>* pSource, const LocalVector* pvCornerValueOldTime, number dt); + + this->template register_update_func(&this_type::template update); + } + { + typedef DimFV1FTGeometry > TGeom; + typedef void (this_type::*TFunc)(const TGeom* geo, + const LocalVector& vCornerValue, + const MathVector vStdVel[], + const bool bStokes, + const DataImport& kinVisco, + const DataImport& density, + const DataImport, dim>* pSource, + const LocalVector* pvCornerValueOldTime, number dt); this->template register_update_func(&this_type::template update); + } } }; diff --git a/incompressible/fv1/two_phase_flow/interface_handler_2pf.h b/incompressible/fv1/two_phase_flow/interface_handler_2pf.h new file mode 100644 index 0000000..4ccc5bc --- /dev/null +++ b/incompressible/fv1/two_phase_flow/interface_handler_2pf.h @@ -0,0 +1,173 @@ +/* + * diffusion_interface_handler_local.h + * + * Created on: 19.01.2015 + * Author: suze + */ + +#ifndef INTERFACE_HANDLER_2PF_H_ +#define INTERFACE_HANDLER_2PF_H_ + +#include "lib_disc/spatial_disc/immersed_util/interface_handler/interface_handler_two_sided_cut/interface_handler_diffusion.h" + +namespace ug{ +namespace NavierStokes{ + + +template +class InterfaceHandlerLocal2PF : public InterfaceHandlerLocalDiffusion +{ + + public: + /// World dimension + static const int dim = TWorldDim; + + /// used boundary face type + typedef typename DimFV1CutGeometry >::BF interfaceBF; + + typedef typename domain_traits::grid_base_object grid_base_object; + + InterfaceHandlerLocal2PF(SmartPtr > interfaceProvider, + SmartPtr > cutElementHandler, + number fluidDensity, number fluidKinVisc); + + virtual ~InterfaceHandlerLocal2PF() {} + + /////////////////////////////////////////////////////////////// + /// redefine base calss methods: + /////////////////////////////////////////////////////////////// + // bool update_elem(GridObject* elem, const MathVector* vCornerCoords, int interfaceOrientation); + + // int CollectCorners_FlatTop_2d(GridObject* elem); + + // size_t get_vertex_index(Vertex* vrt, GridObject* elem); + + + /////////////////////////////////////////////////////////////// + /// new methods: + /////////////////////////////////////////////////////////////// + + size_t num_particles() const { return this->m_spInterfaceProvider->num_particles();} + + /// get solution values + MathVector get_solution(size_t prtIndex, size_t timeSeriesInd) + { return this->m_spInterfaceProvider->get_solution(prtIndex, timeSeriesInd); } + + + number get_density() { return this->m_spInterfaceProvider->get_density(0); } + number get_density_fluid() { return m_fluidDensity; } + number get_kinVisc_fluid() { return m_fluidKinVisc; } + + const LocalIndices& get_local_indices() const { return m_ind; } + + void set_jacobian_tri(const LocalMatrix locJ) { m_locJ_tri = locJ; } + void set_jacobian_quad(const LocalMatrix locJ){ m_locJ_quad = locJ; } + + void set_defect_tri(const LocalVector locD) { m_locD_tri = locD; } + void set_defect_quad(const LocalVector locD){ m_locD_quad = locD; } + + void reset_defect_on_interface(LocalVector& locD, const size_t size); + + void reset_jacobian_on_interface(LocalMatrix& locJ, const size_t size); + + + void set_solution_tri(const LocalVector locU) { m_locU_tri = locU; } + void set_solution_quad(const LocalVector locU){ m_locU_quad = locU; } + + void set_DoF_tag_tri(const bool bFactor2_for_DoFIndex) + { m_shift_DoFIndex_tri = bFactor2_for_DoFIndex; return; } + void set_DoF_tag_quad(const bool bFactor2_for_DoFIndex) + { m_shift_DoFIndex_quad = bFactor2_for_DoFIndex; return; } + + const size_t get_index_shift_tri() const { return m_shift_DoFIndex_tri; } + const size_t get_index_shift_quad() const { return m_shift_DoFIndex_quad; } + + LocalMatrix& get_local_jacobian_tri() { return m_locJ_tri; } + LocalMatrix& get_local_jacobian_quad() { return m_locJ_quad; } + + LocalVector& get_local_defect_tri() { return m_locD_tri; } + LocalVector& get_local_defect_quad() { return m_locD_quad; } + + LocalVector& get_local_solution_tri() { return m_locU_tri; } + LocalVector& get_local_solution_quad() { return m_locU_quad; } + + void set_local_sol(LocalVector& solU, const size_t size, const LocalVector& lvec, const int orientation); + + LocalVector set_jump_values(LocalIndices ind, const size_t size); + LocalVector set_jump_grad_values(LocalIndices ind, const size_t size); + + double get_jump_value_const(const MathVector position); + double get_jump_value(const MathVector position); + double get_jump_value_ex3(const MathVector position); + double get_jump_grad_value(const MathVector position); + double get_jump_grad_value_ex3(const MathVector position); + + double get_source(const MathVector position); + double get_source_kappa(const MathVector position); + + // writes solution of global vector vec into this->m_verticesValue-array: + void set_interface_values(const std::vector verticesValues); + + + void resize_local_data(LocalVector locU) + { + LocalIndices ind = locU.get_indices(); + + for (size_t fct = 0; fct < (dim+1); ++fct) + { + // resize for cut triangle + ind.resize_dof(fct, 3); + m_locU_tri.resize(ind); + m_locD_tri.resize(ind); + m_locJ_tri.resize(ind); + } + for (size_t fct = 0; fct < (dim+1); ++fct) + { + // resize for cut quadrilateral + ind.resize_dof(fct, 4); + m_locU_quad.resize(ind); + m_locD_quad.resize(ind); + m_locJ_quad.resize(ind); + } + + UG_LOG("check LocalIndices ind:" << ind << "\n"); + + return; + } + + private: + + /// fuid parameter imported by Constructor() + number m_fluidDensity; + number m_fluidKinVisc; + + /// size of local algebra for cut element: 'm_numFct' x 'm_numCo' + size_t m_numFct; + /// number of corners ofcut element + size_t m_numCo; + + /// new local algebra for resized cut element + LocalIndices m_ind; + + // local data for assembling: + LocalMatrix m_locJ_tri; + LocalMatrix m_locJ_quad; + LocalVector m_locD_tri; + LocalVector m_locD_quad; + LocalVector m_locU_tri; + LocalVector m_locU_quad; + + // scale factor for access to DoFIndex on triangle or quadri as cut element + // --> for call during 'add_local_def/jac_to_global_interface()': + bool m_shift_DoFIndex_tri; + bool m_shift_DoFIndex_quad; + +}; + +}// namespace NavierStokes +} // end ug namespace + +#include "interface_handler_2pf_impl.h" + + +#endif /* INTERFACE_HANDLER_2PF_H_ */ diff --git a/incompressible/fv1/two_phase_flow/interface_handler_2pf_impl.h b/incompressible/fv1/two_phase_flow/interface_handler_2pf_impl.h new file mode 100644 index 0000000..4644e3d --- /dev/null +++ b/incompressible/fv1/two_phase_flow/interface_handler_2pf_impl.h @@ -0,0 +1,527 @@ +/* + * diffusion_interface_handler_local_impl.h + * + * Created on: 15.01.2015 + * Author: suze + */ + +#ifndef INTERFACE_HANDLER_2PF_IMPL_ +#define INTERFACE_HANDLER_2PF_IMPL_ + +#include + +namespace ug{ +namespace NavierStokes{ + + +//hier methods aus moving_particle_impl.h einfuegen + +// call constructor of base class +template +InterfaceHandlerLocal2PF::InterfaceHandlerLocal2PF( + SmartPtr > interfaceProvider, + SmartPtr > cutElementHandler, + number fluidDensity, number fluidKinVisc) : + InterfaceHandlerLocalDiffusion(interfaceProvider, cutElementHandler), + m_fluidDensity(fluidDensity), + m_fluidKinVisc(fluidKinVisc), + m_numFct(0), + m_numCo(0), + m_shift_DoFIndex_tri(false), + m_shift_DoFIndex_quad(false) +{ +} +; + +template +void InterfaceHandlerLocal2PF:: +set_interface_values(const std::vector verticesValues) +{ + this->m_verticesValue.clear(); + + for (size_t i = 0; i < verticesValues.size(); ++i) + this->m_verticesValue.push_back(verticesValues[i]); + + +// through error: + if ( this->m_verticesValue.size() != verticesValues.size() ) + { + UG_LOG("m_verticesValue.size(): " << this->m_verticesValue.size() << "\n"); + UG_LOG("verticesValues.size(): " << verticesValues.size() << "\n"); + UG_THROW("in InterfaceHandlerLocal2PF::set_interface_values: wrong size of m_verticesValue!\n"); + } + +} + +template +void InterfaceHandlerLocal2PF:: +reset_defect_on_interface(LocalVector& locD, const size_t size) +{ + if ( size > locD.num_all_dof(0) ) + { + UG_LOG("in 'reset_defect_on_interface()': size = " << size << ", locD.size = " << locD.num_all_dof(0) << "\n"); + UG_THROW("in 'reset_defect_on_interface()': size = " << size << ", locD.size = " << locD.num_all_dof(0) << " => claimed size is NOT equal to size of solution vector!\n"); + } +// loop and set solution in 'solU_tri': + for (size_t dof = 0; dof < locD.num_all_dof(0); ++dof) + { + // if dof_real is index of m_vertex: get solution from class: + if ( this->lies_onInterface_size(dof, size) ) + locD.value(0, dof) = 0.0; + } +} + +template +void InterfaceHandlerLocal2PF:: +reset_jacobian_on_interface(LocalMatrix& locJ, const size_t size) +{ + + if ( size > locJ.num_all_row_dof(0) ) + { + UG_LOG("in 'reset_jacobian_on_interface()': size = " << size << ", locJ.size = " << locJ.num_all_row_dof(0) << "\n"); + UG_THROW("in 'reset_jacobian_on_interface()': size = " << size << ", locJ.size = " << locJ.num_all_row_dof(0) << " => claimed size is NOT equal to size of solution vector!\n"); + } +// loop and set solution in 'solU_tri': + for (size_t dof1 = 0; dof1 < locJ.num_all_row_dof(0); ++dof1) + { + if ( this->lies_onInterface_size(dof1, size) ) + { + // erase all col-values of chosen row dof1: + for (size_t dof2 = 0; dof2 < locJ.num_all_col_dof(0); ++dof2) + locJ.value(0, dof1, 0, dof2) = 0.0; + } + + } + +} + +template +double InterfaceHandlerLocal2PF:: +get_jump_value(const MathVector position) +{ + return 0.0; + + double absValue = position[0]*position[0] + position[1]* position[1]; + double sum = position[0] + position[1]; + + double returnValue = log(absValue) - sin(sum); + + return returnValue; +} + +template +double InterfaceHandlerLocal2PF:: +get_jump_value_ex3(const MathVector position) +{ + + //return 0.0; + + if ( this->get_orientation() == 1) + return 0.0; + + double absValue = position[0]*position[0] + position[1]* position[1]; + double returnValue = exp(-absValue); + + return returnValue; +} + +template +double InterfaceHandlerLocal2PF:: +get_jump_value_const(const MathVector position) +{ + return 0.0; + + double absValue = position[0]*position[0] + position[1]* position[1]; + double factor = 8*(2*absValue - position[0] - position[1]); + + return factor*exp(-absValue); + + double sum = position[0] + position[1]; + + double returnValue = log(absValue) - sin(sum); + + return returnValue; +} + +template +double InterfaceHandlerLocal2PF:: +get_jump_grad_value_ex3(const MathVector position) +{ + //return 0.0; + + if ( this->get_orientation() == -1) + return 0.0; + + double absValue = position[0]*position[0] + position[1]* position[1]; + double sum = position[0] + position[1]; + + double returnValue = 8*(2*absValue - sum)*exp(-absValue); + + return returnValue; +} + +template +double InterfaceHandlerLocal2PF:: +get_jump_grad_value(const MathVector position) +{ + if ( this->get_orientation() == 1) + return 2.0; + else + return 0.0; + + double absValue = position[0]*position[0] + position[1]* position[1]; + + return -exp(-absValue); + + double sum = position[0] + position[1]; + MathVector normal; + normal[0] = position[0]; + normal[1] = position[1]; + + double returnValue = 2*(sin(sum) + 2)*(position[0]*normal[0] + position[1]*normal[1])/absValue; + returnValue -= cos(sum)*(cos(sum)+2)*(normal[0] + normal[1]); + + return returnValue; +} + + + +template +double InterfaceHandlerLocal2PF:: +get_source_kappa(const MathVector position) +{ + if ( this->get_orientation() == 1) + { + return 16.0*16.0; + } + else + { + if ( this->get_orientation() != -1) + UG_THROW("wrong orientation!\n"); + + double dist_x = position[0] - 0.1; + double dist_y = position[1] - 0.2; + double dist = sqrt(dist_x*dist_x+dist_y*dist_y); + + return 200*16*dist*dist; + + } + +} + +template +double InterfaceHandlerLocal2PF:: +get_source(const MathVector position) +{ + double absValue = position[0]*position[0] + position[1]* position[1]; + double sum = position[0] + position[1]; +// double dist = sqrt(absValue); + + double returnValue = 0.0; + if ( this->get_orientation() == 1) + returnValue = 2*sum*cos(sum)/absValue; + else + { + returnValue = -4*sin(sum)*(cos(sum)+1); + + if ( this->get_orientation() != -1) + UG_THROW("wrong orientation!\n"); + } + return 2.0; //returnValue; +} + + + +template +LocalVector InterfaceHandlerLocal2PF:: +set_jump_values(LocalIndices ind, const size_t size) +{ + LocalVector jump; + ind.resize_dof(0, size); + jump.resize(ind); + +// loop and set solution in 'solU_tri': + for (size_t dof = 0; dof < jump.num_all_dof(0); ++dof) + { + + // if dof_real is index of m_vertex: get solution from class: + if ( this->lies_onInterface_size(dof, size) ) + { + size_t dof_real = this->real_index_size(dof, size); + jump.value(0, dof) = get_jump_value_ex3(this->get_VerticesPos(dof_real)); + } + else + { + jump.value(0, dof) = 0.0; + } + } + + return jump; +} + + +template +LocalVector InterfaceHandlerLocal2PF:: +set_jump_grad_values(LocalIndices ind, const size_t size) +{ + LocalVector jump_grad; + ind.resize_dof(0, size); + jump_grad.resize(ind); + +// loop and set solution in 'solU_tri': + for (size_t dof = 0; dof < jump_grad.num_all_dof(0); ++dof) + { + + // if dof_real is index of m_vertex: get solution from class: + if ( this->lies_onInterface_size(dof, size) ) + { + size_t dof_real = this->real_index_size(dof, size); + jump_grad.value(0, dof) = get_jump_grad_value_ex3(this->get_VerticesPos(dof_real)); + } + else + { + jump_grad.value(0, dof) = 0.0; + } + } + + return jump_grad; + +} + + +template +void InterfaceHandlerLocal2PF:: +set_local_sol(LocalVector& solU, const size_t size, const LocalVector& lvec, const int orientation) +{ + if ( size > solU.num_all_dof(0) ) + { + UG_LOG("in 'set_local_sol()': size = " << size << ", solU.size = " << solU.num_all_dof(0) << "\n"); + UG_THROW("in 'set_local_sol()': size = " << size << ", solU.size = " << solU.num_all_dof(0) << " => claimed size is NOT equal to size of solution vector!\n"); + } +// loop and set solution in 'solU_tri': + for(size_t fct=0; fct < solU.num_all_fct(); ++fct) + for (size_t dof = 0; dof < solU.num_all_dof(fct); ++dof) + { + size_t dof_real = this->real_index_size(dof, size); + + // if dof_real is index of m_vertex: get solution from class: + if ( this->lies_onInterface_size(dof, size) ) + { + solU.value(fct, dof) = this->get_sol(dof_real); + } + else + { + solU.value(fct, dof) = lvec.value(fct,dof_real); + } + } + +} + +/* +template +int InterfaceHandlerLocal2PF:: +CollectCorners_FlatTop_2d(GridObject* elem) +{ + ////////////////////////////////////////////// + // 1) fill vector with fluid corners: + ////////////////////////////////////////////// + + this->m_vCornerCoords.clear(); + this->m_vInterfaceID.clear(); + this->m_vOriginalCornerID.clear(); + +// buffer vectors for (cornerCoords, cornerIndex) + std::vector, size_t > > vOutsideCorners; + std::vector, size_t > > vInsideCorners; + std::vector, size_t > > vNearIntCorners; + +// collect all vertices of the element + std::vector vVertex; + CollectVertices(vVertex, *this->m_spMG, elem); + + bool isFTVertex = false; + for(size_t i = 0; i < vVertex.size(); ++i) + { + // remember boolian for check, weather there existe at least one vertex, which is FT! + isFTVertex = this->is_onInterfaceVertex(vVertex[i], i); + if ( isFTVertex ) + break; + } + + if ( !isFTVertex ) + UG_THROW("Error in 'CollectCorners_FlatTop_2d': no vertex is FTVertex: should be true for at least 1 vertex!\n"); + + // collect all edges of the element + std::vector vEdges; + CollectEdgesSorted(vEdges, *this->m_spMG, elem); + + // loop vertices + ////////////////////////////////////////////// + // REMARK: + // order is the same as in 'vCornerCoords', therefore we can be sure, that the + // order of the new 'vCornerIBCoords' will be consistent with the grid standard + ////////////////////////////////////////////// + + bool bNearInterface = false; + for(size_t i = 0; i < vVertex.size(); ++i) + { + Vertex* vrtRoot = vVertex[i]; + + ////////////////////////////////////////////// + // case 1: + // vertex insideDomain + if ( !this->is_onInterfaceVertex(vrtRoot, i) ) + { + if ( this->is_nearInterfaceVertex(vrtRoot, i) ) + UG_THROW("NearInterface BUT !is_FT => neuerdings Fehler!!....\n"); + + this->m_vCornerCoords.push_back(this->m_aaPos[vrtRoot]); + this->m_vOriginalCornerID.push_back(i); + + vInsideCorners.push_back(std::make_pair(this->m_aaPos[vrtRoot], i)); + } + ////////////////////////////////////////////// + // case 2: + // vertex = FT + ON interface + // => KEINE Berechnung von 'intersectionPoint' notwendig! -> pushen und alten index pushen + + // REMARK: is_nearInterfaceVerx = false per default, if m_vThresholdOnLevel = 0.0 + else if ( this->is_nearInterfaceVertex(vrtRoot, i) ) + { + bNearInterface = true; + this->m_vCornerCoords.push_back(this->m_aaPos[vrtRoot]); + this->m_vOriginalCornerID.push_back(i); + this->m_vInterfaceID.push_back(this->m_vCornerCoords.size()-1); // attention: push AFTER 'm_vCornerCoords.push_back()'!! + + vOutsideCorners.push_back(std::make_pair(this->m_aaPos[vrtRoot], i)); + vNearIntCorners.push_back(std::make_pair(this->m_aaPos[vrtRoot], i)); + + } + ////////////////////////////////////////////// + // case 3: + // vertex 'outsideFluid' + // => NEUE Position berechen+pushen und alten index pushen + else + { + ////////////////////////////////////////////////////////////////////////////////////////// + // loop alle edges, die interface schneiden und damit einen neuen intersectionPnt + // beitragen zum damit assoziierten alten index + for(size_t e = 0; e < vEdges.size(); ++e) + { + Edge* edge = vEdges[e]; + std::vector vVertexEdge; + CollectVertices(vVertexEdge, *this->m_spMG, edge); + if ( vVertexEdge.size() != 2 ) + UG_THROW("error in collecting vertices associated to an edge!....EXIT!...\n"); + + Vertex* vrt1 = vVertexEdge[0]; + Vertex* vrt2 = vVertexEdge[1]; + size_t vrtInd1 = get_vertex_index(vrt1, elem); + size_t vrtInd2 = get_vertex_index(vrt2, elem); + + MathVector intersectionPnt; + + /////////////////////////////////////////////////////////////////// + // lies vrtRoot on a cutted edge? + /////////////////////////////////////////////////////////////////// + // case1: vrtRoot is intersectionPnt with insideCorner = near_interface_corner => remove! + if ( this->is_nearInterfaceVertex(vrt2, vrtInd2) || this->is_nearInterfaceVertex(vrt1, vrtInd1) ) + { bNearInterface = true; continue; } + // case2: vert2 = outsideParticle && vrt1 = insideParticle: + else if ( vrtRoot == vrt1 && !this->is_onInterfaceVertex(vrt2, vrtInd2) ){ + this->get_intersection_point(intersectionPnt, vrt2, vrt1); + } + // case3: vrt1 = outsideParticle && vrt2 = insideParticle: + else if ( vrtRoot == vrt2 && !this->is_onInterfaceVertex(vrt1, vrtInd1) ) + this->get_intersection_point(intersectionPnt, vrt1, vrt2); + else + continue; + + // check for correct inersectionPnt + if ( fabs(this->get_LSvalue_byPosition(intersectionPnt)) > 1e-6 ) + UG_THROW("in 'CollectIBCorners2d()': Error in computation of 'intersectionPnt':\n " + " intersectionPnt = " << intersectionPnt << "\n distance from interace = " << fabs(get_LSvalue_byPosition(intersectionPnt)) << "\n"); + + /////////////////////////////////////////////////////////////////// + // only push_back, if not included yet! + // -> can be ONLY the case, if the intersectionPoint is a node + if ( ! this->isIncluded(this->m_vCornerCoords, intersectionPnt) ) + { + + this->m_vCornerCoords.push_back(intersectionPnt); + this->m_vOriginalCornerID.push_back(i); + this->m_vInterfaceID.push_back(this->m_vCornerCoords.size()-1); // attention: push AFTER 'm_vCornerCoords.push_back()'!! + + vOutsideCorners.push_back(std::make_pair(intersectionPnt, i)); + } + + + } // end edge-loop + + } // end else-case + + } // end vrt-loop + +//////////////////////////////////////////////////////////////////////////////////////////// +// Postprecessing for quadrilaterals ( <=> vOutsideCorners == 2 ) +// (vInsideCorners.size() == 2) && (bNearInterface) => ALL nodes insideFluid, BUT one ON surface +// => no Quadrilateral, but Triangle!! +//////////////////////////////////////////////////////////////////////////////////////////// + MathVector normalDir(0.0); + if ( (this->m_vCornerCoords.size() == 4) && (!bNearInterface) && (dim == 2) ) + this->ResortQuadrilateral(vInsideCorners, vOutsideCorners, normalDir); + else if ( bNearInterface ) + { + // Quadrilateral -> Triangle + if ( vInsideCorners.size() == 1 ) // case 1 + { + // do nothing, since re-sorting not necessary...??? + } + // skip whole element, since only FT points are included + else if ( vInsideCorners.size() == 0 ) + UG_THROW("in 'CollectCorners_FlatTop_2d()': vInsideCorners.size() " + "= " << vInsideCorners.size() << "not possible!\n"); + } + + + return this->m_vCornerCoords.size(); + +} + + +// called by geo.update()!! +template +bool InterfaceHandlerLocal2PF:: +update_elem(GridObject* elem, const MathVector* vCornerCoords, int interfaceOrientation) +{ + bool do_update_local = false; + this->m_vBF.clear(); + +// computing the cut element modus + this->m_elemModus = compute_element_modus(elem, interfaceOrientation); + + switch(this->m_elemModus) + { + case INSIDE_DOM: if ( dim == 2 ) this->set_element_data(elem, vCornerCoords, ROID_TRIANGLE); + if ( dim == 3 ) this->set_element_data(elem, vCornerCoords, ROID_TETRAHEDRON); + break; // usual assembling + case OUTSIDE_DOM: if ( dim == 2 ) this->set_element_data(elem, vCornerCoords, ROID_TRIANGLE); + if ( dim == 3 ) this->set_element_data(elem, vCornerCoords, ROID_TETRAHEDRON); + break; // usual assembling + case CUT_BY_INTERFACE: this->compute_cut_element_data(elem); + //if ( m_roid == ROID_PYRAMID )UG_THROW("PYRAMID\n"); + do_update_local = true; + break; // cut element assembling + default: + throw(UGError("Error in InterfaceHandlerLocalDiffusion::update(): switch(m_elemModus)!")); + } + + return do_update_local; + +} +*/ + +} // namespace NavierStokes +} // end ug namespace + +#endif /* INTERFACE_HANDLER_2PF_IMPL_ */ diff --git a/incompressible/fv1/two_phase_flow/loc_to_glob_mapper_2pf.h b/incompressible/fv1/two_phase_flow/loc_to_glob_mapper_2pf.h new file mode 100644 index 0000000..ef2048d --- /dev/null +++ b/incompressible/fv1/two_phase_flow/loc_to_glob_mapper_2pf.h @@ -0,0 +1,127 @@ +/* + * diffusion_interface_handler_local.h + * + * Created on: 19.01.2015 + * Author: suze + */ + +#ifndef MAPPER_2PF_H_ +#define MAPPER_2PF_H_ + +#include "lib_disc/spatial_disc/immersed_util/interface_handler/interface_handler_base.h" + +namespace ug{ +namespace NavierStokes{ + + +template +class InterfaceMapper2PF : public IInterfaceMapper +{ + + public: + /// World dimension + static const int dim = TDomain::dim; + /// Algebra type + typedef TAlgebra algebra_type; + + /// Type of algebra matrix + typedef typename algebra_type::matrix_type matrix_type; + + /// Type of algebra vector + typedef typename algebra_type::vector_type vector_type; + + /// Type of geometric base object + typedef typename domain_traits::grid_base_object grid_base_object; + + InterfaceMapper2PF(){}; + + InterfaceMapper2PF(SmartPtr > localHandler) + : m_spInterfaceHandlerLocal(localHandler), + m_numGridNodes(0), + m_resized(false), + m_resized_defect(false), + m_scaleDoFs(false) + {} + + virtual ~InterfaceMapper2PF() {} + + + /// send local entries to global rhs + void add_local_vec_to_global(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd); + void add_local_vec_to_global_interface(vector_type& vec, const LocalVector& lvec, + ConstSmartPtr dd); + + /// send local entries to global matrix + void add_local_mat_to_global(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd); + void add_local_mat_to_global_interface(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd); + + void adjust_mat_global(matrix_type& mat, const LocalMatrix& lmat, + ConstSmartPtr dd){}; + + /// modifies local solution vector for adapted defect computation + void modify_LocalData(LocalMatrix& locJ, LocalVector& locU, + ConstSmartPtr dd){}; + void modify_LocalData(LocalVectorTimeSeries& uT, LocalMatrix& locJ, LocalVector& locU, + ConstSmartPtr dd){}; + + void modify_LocalData(LocalVector& locD, LocalVector& tmpLocD, LocalVector& locU, + ConstSmartPtr dd){}; + void modify_LocalData(LocalVectorTimeSeries& uT, LocalVector& locD, LocalVector& tmpLocD, LocalVector& locU, + ConstSmartPtr dd, size_t t){}; + + /// modifies global solution vector for adapted defect computation + void modify_GlobalSol(vector_type& vecMod, const vector_type& vec, + ConstSmartPtr dd); + + void modify_GlobalSol(SmartPtr > vSolMod, + ConstSmartPtr > vSol, + ConstSmartPtr dd){}; + + /////////////////////////////////////////////////////////////// + /// new methods: + /////////////////////////////////////////////////////////////// + void set_identity_mat_constraint(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd); + + LocalMatrix& get_local_jacobian_tri() + { return m_spInterfaceHandlerLocal->get_local_jacobian_tri(); } + LocalMatrix& get_local_jacobian_quad() + { return m_spInterfaceHandlerLocal->get_local_jacobian_quad(); } + + LocalVector& get_local_defect_tri() + { return m_spInterfaceHandlerLocal->get_local_defect_tri(); } + LocalVector& get_local_defect_quad() + { return m_spInterfaceHandlerLocal->get_local_defect_quad(); } + + void set_interface_values(const std::vector verticesValues) + { m_spInterfaceHandlerLocal->set_interface_values(verticesValues); } + + // called during init() of diffusionInterface: + void set_numDoFs(const size_t numDoFs) + { m_numGridNodes = numDoFs;} + void set_numNewDoFs(const size_t numNewDoFs) + { m_numNewDoFs = numNewDoFs;} + + void set_bScaleDoFs(bool bScaleDoF) { m_scaleDoFs = bScaleDoF; } + + private: + SmartPtr > m_spInterfaceHandlerLocal; + // number of DoFs in global matrix + size_t m_numGridNodes; + size_t m_numNewDoFs; + bool m_resized; + bool m_resized_defect; + bool m_scaleDoFs; + + +}; + +}// namespace NavierStokes +} // end ug namespace + +#include "loc_to_glob_mapper_2pf_impl.h" + + +#endif /* MAPPER_2PF_H_ */ diff --git a/incompressible/fv1/two_phase_flow/loc_to_glob_mapper_2pf_impl.h b/incompressible/fv1/two_phase_flow/loc_to_glob_mapper_2pf_impl.h new file mode 100644 index 0000000..5561217 --- /dev/null +++ b/incompressible/fv1/two_phase_flow/loc_to_glob_mapper_2pf_impl.h @@ -0,0 +1,379 @@ +/* + * diffusion_interface_handler_local_impl.h + * + * Created on: 15.01.2015 + * Author: suze + */ + +#ifndef MAPPER_2PF_IMPL_ +#define MAPPER_2PF_IMPL_ + + +namespace ug{ +namespace NavierStokes{ + + +template +void InterfaceMapper2PF:: +set_identity_mat_constraint(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd) +{ + DoFIndex index; + + for (size_t i = 0; i < 18; ++i) + { + index = DoFIndex(85 + i,0); + + BlockRef(mat(index, index), 0, 0) = 1.0; + } + +} + + + +template +void InterfaceMapper2PF:: +modify_GlobalSol(vector_type& vecMod, const vector_type& vec, ConstSmartPtr dd) +{ + + size_t numDoFs = vecMod.size(); + const size_t numNewDoFs = numDoFs - m_numGridNodes; + + UG_LOG("---------------modify_GlobalSol--------------\n"); + UG_LOG(" vecMod.size(): " << numDoFs << "\n"); + UG_LOG(" vec.size(): " << vec.size() << "\n"); + UG_LOG("m_numGridNodes: " << m_numGridNodes << "\n"); + UG_LOG(" computed numNewDoFs: " << numNewDoFs << "\n"); + UG_LOG(" m_numNewDoFs: " << m_numNewDoFs << "\n"); + + + DoFIndex index; + std::vector verticesValue; + verticesValue.clear(); + + // numNewDoFs enthaelt schon fact *fct! --> siehe MovingInterface2PF.init() + for (size_t i = 0; i < numNewDoFs; ++i) + { + index = DoFIndex(m_numGridNodes + i, 0); + double value = DoFRef(vec, index); + + verticesValue.push_back(value); + } + + + UG_LOG(" computed numNewDoFs: " << numNewDoFs << "\n"); + UG_LOG("length of verticesValue should be " << numNewDoFs << " = " << numNewDoFs << "\n"); + + if ( numNewDoFs != verticesValue.size() ) + { + UG_LOG(" computed numNewDoFs: " << numNewDoFs << "\n"); + UG_THROW("length of verticesValue should be " << numNewDoFs << " = " << numNewDoFs << "\n"); + } + + // call InterfaceHandlerLocal-method: + // no not doing it! Done locally in add_def: locU_tri and locU_quad: + set_interface_values(verticesValue); + +} + +template +void InterfaceMapper2PF:: +add_local_mat_to_global(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd) +{ + bool print = false; + +// resize global matrix dynamically: + const size_t numDoFs = mat.num_rows(); + + if ( print ) UG_LOG("*** vorher: vec.size(): " << numDoFs << "\n"); + + size_t numAllDoFs = m_numGridNodes + m_numNewDoFs; + if ( m_scaleDoFs ) + numAllDoFs = m_numGridNodes + 2*m_numNewDoFs; + + const int diffDoFs = numAllDoFs - numDoFs; + +// resize global defect ONCE: + if ( diffDoFs > 0 ) + { + mat.resize_and_keep_values(numAllDoFs, numAllDoFs); + if ( print ) + { + UG_LOG("*** m_numGridNodes: " << m_numGridNodes << "\n"); + UG_LOG("*** m_numNewDoFs: " << m_numNewDoFs << "\n"); + UG_LOG("*** numAllDoFs: " << numAllDoFs << "\n"); + UG_LOG("*** nachher: mat.num_rows(): " << mat.num_rows() << "\n"); + } + } + else if ( diffDoFs == 0 ) + { if ( print ) UG_LOG("no resizing!\n");} + else if ( diffDoFs < 0 ) + { + if ( print ) UG_LOG("diffDoFs = " << diffDoFs << "\n"); + UG_THROW("error in add_local_mat_to_global: diffDofs < 0\n"); + } + + + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + + switch(modus) + { + case OUTSIDE_DOM: + AddLocalMatrixToGlobal(mat, lmat); + break; + case INSIDE_DOM: + AddLocalMatrixToGlobal(mat, lmat); + break; + case CUT_BY_INTERFACE: + add_local_mat_to_global_interface(mat, lmat, dd); + break; + default: + throw(UGError("Error in IInterfaceMapper::add_local_mat_to_global()!")); + + } + +} + + +template +void InterfaceMapper2PF:: +add_local_vec_to_global(vector_type& vec, const LocalVector& lvec, ConstSmartPtr dd) +{ + bool print = false; + + const size_t numDoFs = vec.size(); + + if ( print ) UG_LOG("*** vorher: vec.size(): " << numDoFs << "\n"); + + size_t numAllDoFs = m_numGridNodes + m_numNewDoFs; + if ( m_scaleDoFs ) + numAllDoFs = m_numGridNodes + 2*m_numNewDoFs; + + const int diffDoFs = numAllDoFs - numDoFs; + +// resize global defect ONCE: + if ( diffDoFs > 0 ) + { + vec.resize(numAllDoFs, true); + vec.set(0.0); + + if ( print ) + { + UG_LOG("*** m_numGridNodes: " << m_numGridNodes << "\n"); + UG_LOG("*** m_numNewDoFs: " << m_numNewDoFs << "\n"); + UG_LOG("*** numAllDoFs: " << numAllDoFs << "\n"); + UG_LOG("*** nachher: vec.size(): " << vec.size() << "\n"); + } + } + else if ( diffDoFs == 0 ) + { if ( print ) UG_LOG("no resizing!\n");} + else if ( diffDoFs < 0 ) + { + if ( print ) UG_LOG("diffDoFs = " << diffDoFs << "\n"); + UG_THROW("error in add_local_vec_to_global: diffDofs < 0\n"); + } + + + ElementModus modus = m_spInterfaceHandlerLocal->elementModus(); + + switch(modus) + { + case OUTSIDE_DOM: + AddLocalVector(vec, lvec); + break; + case INSIDE_DOM: + AddLocalVector(vec, lvec); + break; + case CUT_BY_INTERFACE: + add_local_vec_to_global_interface(vec, lvec, dd); + break; + default: + throw(UGError("Error in IInterfaceMapper::add_local_vec_to_global()!")); + + } +} + +//get_real_index(dof): +// if dof NOT on interface: returns orig corner index +// if dof ON interface: returns index of m_vertex-array + +template +void InterfaceMapper2PF:: +add_local_mat_to_global_interface(matrix_type& mat, const LocalMatrix& lmat, ConstSmartPtr dd) +{ + UG_LOG("------------------------ START InterfaceMapper2PF ------------------------\n"); + + const LocalIndices& rowInd = lmat.get_row_indices(); + const LocalIndices& colInd = lmat.get_col_indices(); + + DoFIndex indexRow, indexCol; + + /////////////////////////////////////////////////////////////// + /// FIRST: add loc to glob for locJ_tri: + /////////////////////////////////////////////////////////////// + const LocalMatrix& locJ_tri = get_local_jacobian_tri(); + const bool shift_global_index_tri = m_spInterfaceHandlerLocal->get_index_shift_tri(); + + size_t numAllDoFs = m_numGridNodes; + + if ( shift_global_index_tri ) + numAllDoFs = m_numGridNodes + m_numNewDoFs; + +// UG_LOG("in InterfaceMapper2PF::add_loc_mat(): locJ_tri = " << locJ_tri << "\n"); + + for(size_t fct1=0; fct1 < locJ_tri.num_all_row_fct(); ++fct1) + for (size_t dof1 = 0; dof1 < locJ_tri.num_all_row_dof(fct1); ++dof1) + { + const size_t dof1_real = m_spInterfaceHandlerLocal->real_index_tri(dof1); + + // if dof1_real is index of m_vertex: compute global index: + if ( m_spInterfaceHandlerLocal->lies_onInterface_tri(dof1) ) + indexRow = DoFIndex(numAllDoFs + dof1_real, fct1); + else + indexRow = DoFIndex(rowInd.index(fct1, dof1_real), rowInd.comp(fct1, dof1_real)); + + for(size_t fct2=0; fct2 < locJ_tri.num_all_col_fct(); ++fct2) + for (size_t dof2 = 0; dof2 < locJ_tri.num_all_col_dof(fct2); ++dof2) + { + const size_t dof2_real = m_spInterfaceHandlerLocal->real_index_tri(dof2); + + // if dof1_real is index of m_vertex: compute global index: + if ( m_spInterfaceHandlerLocal->lies_onInterface_tri(dof2) ) + indexCol = DoFIndex(numAllDoFs + dof2_real, fct2); + else + indexCol = DoFIndex(colInd.index(fct2, dof2_real), colInd.comp(fct2, dof2_real)); + + // finally add loc to glob: + DoFRef(mat, indexRow, indexCol) += locJ_tri.value(fct1, dof1, fct2, dof2); + + if ( indexRow[0] == 0 || indexRow[0] == 30 ){ + UG_LOG("------------------------ tri: += " << locJ_tri.value(0, dof1, 0, dof2) << "\n"); + UG_LOG("(indexRow, indexCol) = " << indexRow[0] << "," << indexCol[0] << "\n"); + } + } + } + + /////////////////////////////////////////////////////////////// + /// SECOND: add loc to glob for locJ_tri: + /////////////////////////////////////////////////////////////// + const LocalMatrix& locJ_quad = get_local_jacobian_quad(); + const bool shift_global_index_quad = m_spInterfaceHandlerLocal->get_index_shift_quad(); + + // reset numAllDoFs! + numAllDoFs = m_numGridNodes; + + if ( shift_global_index_quad ) + numAllDoFs = m_numGridNodes + m_numNewDoFs; + +// UG_LOG("in InterfaceMapper2PF::add_loc_mat(): locJ_quad = " << locJ_quad << "\n"); + + for(size_t fct1=0; fct1 < locJ_quad.num_all_row_fct(); ++fct1) + for (size_t dof1 = 0; dof1 < locJ_quad.num_all_row_dof(fct1); ++dof1) + { + size_t dof1_real = m_spInterfaceHandlerLocal->real_index_quad(dof1); + + // if dof1_real is index of m_vertex: compute global index: + if ( m_spInterfaceHandlerLocal->lies_onInterface_quad(dof1) ) + indexRow = DoFIndex(numAllDoFs + dof1_real, fct1); + else + indexRow = DoFIndex(rowInd.index(fct1, dof1_real), rowInd.comp(fct1, dof1_real)); + + for(size_t fct2=0; fct2 < locJ_quad.num_all_col_fct(); ++fct2) + for (size_t dof2 = 0; dof2 < locJ_quad.num_all_col_dof(fct2); ++dof2) + { + size_t dof2_real = m_spInterfaceHandlerLocal->real_index_quad(dof2); + + // if dof1_real is index of m_vertex: compute global index: + if ( m_spInterfaceHandlerLocal->lies_onInterface_quad(dof2) ) + indexCol = DoFIndex(numAllDoFs + dof2_real, fct2); + else + indexCol = DoFIndex(colInd.index(fct2, dof2_real), colInd.comp(fct2, dof2_real)); + + // finally add loc to glob: + DoFRef(mat, indexRow, indexCol) += locJ_quad.value(fct1, dof1, fct2, dof2); + + if ( indexRow[0] == 0 || indexRow[0] == 30){ + UG_LOG("------------------------ quad: += " << locJ_quad.value(0, dof1, 0, dof2) << "\n"); + UG_LOG("(indexRow, indexCol) = " << indexRow[0] << "," << indexCol[0] << "\n"); + } + } + } + + UG_LOG("------------------------ END InterfaceMapper2PF ------------------------\n"); + +} + +template +void InterfaceMapper2PF:: +add_local_vec_to_global_interface(vector_type& vec, const LocalVector& lvec, ConstSmartPtr dd) +{ + DoFIndex index_print; + + const LocalIndices& ind = lvec.get_indices(); + DoFIndex index; + + /////////////////////////////////////////////////////////////// + /// FIRST: add loc to glob for locD_tri: + /////////////////////////////////////////////////////////////// + const LocalVector& locD_tri = get_local_defect_tri(); + const bool shift_global_index_tri = m_spInterfaceHandlerLocal->get_index_shift_tri(); + + size_t numAllDoFs = m_numGridNodes; + + if ( shift_global_index_tri ) + { numAllDoFs = m_numGridNodes + m_numNewDoFs; + UG_LOG("it is shifted!\n"); + } +// UG_LOG("in InterfaceMapper2PF::add_loc_vec(): locD_tri = " << locD_tri << "\n"); + + for(size_t fct=0; fct < locD_tri.num_all_fct(); ++fct) + for (size_t dof = 0; dof < locD_tri.num_all_dof(fct); ++dof) + { + const size_t dof_real = m_spInterfaceHandlerLocal->real_index_tri(dof); + + // if dof_real is index of m_vertex: compute global index: + if ( m_spInterfaceHandlerLocal->lies_onInterface_tri(dof) ) + index = DoFIndex(numAllDoFs + dof_real,fct); + else + index = DoFIndex(ind.index(fct, dof_real), ind.comp(fct, dof_real)); + + // finally add loc to glob: + DoFRef(vec, index) += locD_tri.value(fct, dof); + + } + + + /////////////////////////////////////////////////////////////// + /// SECOND: add loc to glob for locU_quad: + /////////////////////////////////////////////////////////////// + const LocalVector& locD_quad = get_local_defect_quad(); + const bool shift_global_index_quad = m_spInterfaceHandlerLocal->get_index_shift_quad(); + +// reset numAllDoFs! + numAllDoFs = m_numGridNodes; + + if ( shift_global_index_quad ) + numAllDoFs = m_numGridNodes + m_numNewDoFs; + +// UG_LOG("in InterfaceMapper2PF::add_loc_vec(): locD_quad = " << locD_quad << "\n"); + + for(size_t fct=0; fct < locD_quad.num_all_fct(); ++fct) + for (size_t dof = 0; dof < locD_quad.num_all_dof(0); ++dof) + { + size_t dof_real = m_spInterfaceHandlerLocal->real_index_quad(dof); + + // if dof_real is index of m_vertex: compute global index: + if ( m_spInterfaceHandlerLocal->lies_onInterface_quad(dof) ) + index = DoFIndex(numAllDoFs + dof_real,fct); + else + index = DoFIndex(ind.index(fct, dof_real), ind.comp(fct, dof_real)); + + // finally add loc to glob: + DoFRef(vec, index) += locD_quad.value(fct, dof); + } + +} + +} // namespace NavierStokes +} // end ug namespace + +#endif /* MAPPER_2PF_IMPL_ */ diff --git a/incompressible/fv1/two_phase_flow/two_phase_flow.h b/incompressible/fv1/two_phase_flow/two_phase_flow.h new file mode 100644 index 0000000..c2d232b --- /dev/null +++ b/incompressible/fv1/two_phase_flow/two_phase_flow.h @@ -0,0 +1,200 @@ +/* + * diffusion_interface.h + * + * Created on: 24.08.2017 + * Author: suze + */ + +#ifndef PF2_INTERFACE_H_ +#define PF2_INTERFACE_H_ + + +#ifdef UG_PARALLEL + #include "lib_grid/parallelization/load_balancer_util.h" +#endif + +#include "../navier_stokes_fv1.h" +#include "../../../navier_stokes_base.h" +#include "interface_handler_2pf.h" +#include "loc_to_glob_mapper_2pf.h" +#include "lib_disc/spatial_disc/immersed_util/immersed_interface_base.h" + +namespace ug{ +namespace NavierStokes{ + + + +template < typename TDomain, typename TAlgebra> +class MovingInterface2PF + : public IImmersedInterface +{ + public: + /// world Dimension + static const int dim = TDomain::dim; + + /// Algebra type + typedef TAlgebra algebra_type; + + /// Type of algebra matrix + typedef typename algebra_type::matrix_type matrix_type; + + /// Type of algebra vector + typedef typename algebra_type::vector_type vector_type; + + typedef typename domain_traits::grid_base_object grid_base_object; + + MovingInterface2PF( + SmartPtr > ass, + SmartPtr > spMaster, + SmartPtr > interfaceProvider, + SmartPtr > cutElementHandler, + number fluidDensity1, number fluidDensity2); + + void set_StdFV_assembling(bool bValue) { m_spInterfaceHandlerLocal->set_StdFV_assembling(bValue);} + bool StdFV_assembling() { return m_spInterfaceHandlerLocal->StdFV_assembling(); } + + + // destructor + ~MovingInterface2PF(){}; + + /// called via .lua: + void initialize_threshold(TDomain& domain, const int baseLevel, const int topLevel); + void set_threshold(size_t level, const number threshold) + { m_spCutElementHandler->set_threshold(level, threshold); } + + ////////////////////////////////////////////////////////////////////////////////// + /// Info - 'initialize_interface()': + /// + /// computes vertices on intersection of cut element edges and interface: + /// for 2d: instead of computing intersections: count number of cut elements! + /// -> #cutElements == #m_vertices + /// -> called during init() + ////////////////////////////////////////////////////////////////////////////////// + + const size_t initialize_interface(vector_type& u, ConstSmartPtr dd); + + + number MeanElementDiameter(TDomain& domain, int level); + + ////////////////////////////////////////////////////////////////////////////////// + // ---> .lua: update(u, deltaT, u:grid_level()): update global indices (transInd...) + // => A. copy_solution(topLev) + // B. update(baseLev-topLev) + // C. update_solution(topLev) + ////////////////////////////////////////////////////////////////////////////////// + // write solution to nodes outside fluid with particle velocities + // --> call method vie .lua BEFORE 'solTimeSeries:push_discard_oldest(oldestSol, time)': + // => in case that outside nodes are inside AFTER update_prtCoords: NO solution defined here! + + + /// call of the method via lua to set the real velocity values within the particle domain + void adjust_global_solution(vector_type& u, const int topLevel); + void fill_particle_solution(vector_type& u, const int topLevel, const number time); + void update(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, const int topLevel, const number time) + { + int topLev = spApproxSpace->num_levels()-1; + if ( topLev != topLevel ) + UG_THROW("MovingParticle::update: parameter 'topLevel' = " << topLevel << " != " + << topLev << "current top leven! \n"); + + // fill particle nodes with their real solution + fill_particle_solution(u, topLevel, time); + + // update data: bool_marker + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + m_spCutElementHandler->template init(dd, baseLevel, topLevel); + + } + + + void set_analytic_solution(vector_type& u, SmartPtr > spApproxSpace, SmartPtr mg, const int topLevel); + void adjust_for_error(vector_type& u, vector_type& uCopy, SmartPtr > spApproxSpace, SmartPtr mg, const int topLevel); + + double compute_solution_value(const MathVector& vrtPos); + + number get_L2Error() + { return m_spInterfaceHandlerLocal->get_L2Error(); } + + void init(vector_type& u, SmartPtr > spApproxSpace, const int baseLevel, const int topLevel, bool bScaleDoFs) + { + m_spApproxSpace = spApproxSpace; + + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + + + size_t numDoFs = u.size(); + const size_t num_interfaceDoFs = initialize_interface(u, dd); + const size_t num_newDoFs = 4*num_interfaceDoFs; + + m_spInterfaceMapper->set_numDoFs(numDoFs); + m_spInterfaceMapper->set_numNewDoFs(num_interfaceDoFs); + + UG_LOG("________________ numDoFs = " << numDoFs << "\n"); + UG_LOG("________________ num_interfaceDoFs = " << num_interfaceDoFs << "\n"); + UG_LOG("________________ num_newDoFs = " << num_newDoFs << "\n"); + + // values for new DoFs are set to 0.0 by the 'resize()'-method (see vector.h): + if ( bScaleDoFs ) + u.resize(numDoFs + 2*num_newDoFs); + else + u.resize(numDoFs + num_newDoFs); + + UG_LOG("AGAIN: in init(): numALLDoFs = " << u.size() << "\n"); + + m_spInterfaceMapper->set_bScaleDoFs(bScaleDoFs); + m_spInterfaceHandlerLocal->set_bScaleDoFs(bScaleDoFs); + + m_spInterfaceHandlerLocal->L2Error_init(); + + // not necessary anymore: only local evaluations within diffusion problem! + //m_spCutElementHandler->template init_marker(dd, baseLevel, topLevel); + } + + /// checks if grid data is updated and returns 'levIndex'-pair for 'gridLevel' in 'm_Map' + int get_Index(const GridLevel& gridLevel) + { + ConstSmartPtr dd = m_spApproxSpace->dof_distribution(gridLevel); + + const int levIndex = m_spCutElementHandler->get_Index(gridLevel, dd); + + return levIndex; + } + + //ToDo: method needed? + void update_interface( const int topLevel, number deltaT); + + bool is_time_dependent() { return m_spInterfaceHandlerLocal->is_time_dependent();} + + void interpolate_point(ConstSmartPtr dd, const vector_type& u, + const MathVector& evalPos, MathVector& interpolation); + + void print_deltaP(const vector_type& u, const int topLevel); + void print_pressure(const vector_type& u, const int topLevel); + void print_pressure_nodal(const vector_type& u, const int topLevel); + + /// writing data to file; called via .lua + void print_velocity(const vector_type& u, const int topLevel, number time, const char* filename); + + private: + /// current ApproxSpace + SmartPtr > m_spApproxSpace; + + SmartPtr > m_spInterfaceProvider; + SmartPtr > m_spCutElementHandler; + SmartPtr > m_spInterfaceHandlerLocal; + + SmartPtr > m_spInterfaceMapper; + +}; + +} // end namespace NavierStokes +} // end namespace ug + + +#include "two_phase_flow_impl.h" + +#endif /* PF2_INTERFACE_H_ */ + + + + diff --git a/incompressible/fv1/two_phase_flow/two_phase_flow_impl.h b/incompressible/fv1/two_phase_flow/two_phase_flow_impl.h new file mode 100644 index 0000000..2a9d4d7 --- /dev/null +++ b/incompressible/fv1/two_phase_flow/two_phase_flow_impl.h @@ -0,0 +1,320 @@ + +/* + * two_phase_flow_impl.h + * + * Created on: 24.08.2017 + * Author: suze + */ + +#ifndef PF2_INTERFACE_IMPL_ +#define PF2_INTERFACE_IMPL_ + + +namespace ug { +namespace NavierSTokes { + +/////////////////////////////////////////////////////////// +// Implementation of the methods class +// 'MovingInterface2PF' +/////////////////////////////////////////////////////////// + + +template +MovingInterface2PF::MovingInterface2PF( + SmartPtr > ass, + SmartPtr > spMaster, SmartPtr > interfaceProvider, + SmartPtr > cutElementHandler, + number fluidDensity1, number fluidDensity2) : + m_spInterfaceProvider(interfaceProvider), + m_spInterfaceHandlerLocal(new InterfaceHandlerLocal2PF(interfaceProvider, cutElementHandler, fluidDensity1, fluidDensity2)), + m_spCutElementHandler(cutElementHandler), + m_spInterfaceMapper(new InterfaceMapper2PF (m_spInterfaceHandlerLocal)) +{ + if (interfaceProvider->num_particles() == 0) + UG_THROW("MovingParticle::Constructor(): no particles initializen in 'globalHandler\n"); + + // initialize singleton and set local handler + typedef DimFV1CutGeometry > TFVGeom; + TFVGeom& geo = GeomProvider::get(LFEID(LFEID::LAGRANGE, dim, 1),1); + geo.set_interface_handler(m_spInterfaceHandlerLocal); + + // initialize mapper within domainDisc: + SmartPtr > assAdapt = ass->ass_tuner(); + assAdapt->set_mapping(m_spInterfaceMapper.get()); + + assAdapt->enable_modify_solution(true); + // => assTuner->modify_LocSol() = mapper->modify_LocSol() + // see: ass_tuner.h: 114 + + +} + +template +double MovingInterface2PF:: +compute_solution_value(const MathVector& vrtPos) +{ + double kappa_1 = 1.0; + double kappa_2 = 10.0; + double sqR = 0.4*0.4; + double dist_x = vrtPos[0] - 0.1; + double dist_y = vrtPos[1] - 0.2; + double sqDist = dist_x*dist_x+dist_y*dist_y; + + double value = -4*kappa_1*kappa_2*kappa_2*sqR*sqDist + 2*sqR*sqR*kappa_2*(2*kappa_1*kappa_2 - 1); + + double dist = sqrt(sqDist); + if ( dist > 0.4 ) + { + value = -2*kappa_2*sqDist*sqDist; + UG_LOG("value = " << value << "\n"); + } + return value; +} + +template +void MovingInterface2PF:: +set_analytic_solution(vector_type& u, SmartPtr > spApproxSpace, SmartPtr mg, const int topLevel) +{ + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + + typedef MathVector position_type; + typedef Attachment position_attachment_type; + typedef Grid::VertexAttachmentAccessor position_accessor_type; + + position_attachment_type m_aPos = GetDefaultPositionAttachment(); + position_accessor_type m_aaPos; + + if(!mg->has_attachment(m_aPos)) + mg->attach_to(m_aPos); + m_aaPos.access(*mg, m_aPos); + + typedef typename domain_traits::grid_base_object grid_base_object; + + typename DoFDistribution::traits::const_iterator iter, iterEnd; + iter = dd->template begin(); + iterEnd = dd->template end(); + + // loop elements in order to compute the volume and set rhs: + for( ; iter != iterEnd; iter++) + { + // get element + grid_base_object* elem = *iter; + std::vector > vCornerCoords; + CollectCornerCoordinates(vCornerCoords, *elem, m_aaPos); + + std::vector ind; + dd->dof_indices(elem, 0, ind); + + // loop vertices + for(size_t i = 0; i < elem->num_vertices(); ++i) + { + // get vertex +// Vertex* vrt = elem->vertex(i); + double value = compute_solution_value(vCornerCoords[i]); + DoFRef(u, ind[i]) = value; + } + } + +} + +template +void MovingInterface2PF:: +adjust_for_error(vector_type& u, vector_type& uCopy, SmartPtr > spApproxSpace, SmartPtr mg, const int topLevel) +{ + + + size_t numDoFs = u.size(); + UG_LOG("domain disc: numDoFs = " << numDoFs << "\n"); + size_t numDoFsCopy = uCopy.size(); + UG_LOG("domain disc: numDoFsCopy = " << numDoFsCopy << "\n"); + + u.resize(numDoFsCopy); + + numDoFs = u.size(); + UG_LOG("**domain disc: numDoFs = " << numDoFs << "\n"); + + + + ConstSmartPtr dd = spApproxSpace->dof_distribution(GridLevel(topLevel, GridLevel::LEVEL)); + + typedef MathVector position_type; + typedef Attachment position_attachment_type; + typedef Grid::VertexAttachmentAccessor position_accessor_type; + + position_attachment_type m_aPos = GetDefaultPositionAttachment(); + position_accessor_type m_aaPos; + + if(!mg->has_attachment(m_aPos)) + mg->attach_to(m_aPos); + m_aaPos.access(*mg, m_aPos); + + typedef typename domain_traits::grid_base_object grid_base_object; + + typename DoFDistribution::traits::const_iterator iter, iterEnd; + iter = dd->template begin(); + iterEnd = dd->template end(); + + // loop elements in order to compute the volume and set rhs: + for( ; iter != iterEnd; iter++) + { + // get element + grid_base_object* elem = *iter; + + ElementModus elemModus = m_spInterfaceHandlerLocal->compute_element_modus(elem); + bool do_adjust = false; + + switch(elemModus) + { + case INSIDE_DOM: break; + case OUTSIDE_DOM: break; + + case CUT_BY_INTERFACE: do_adjust = true; break; + default: + throw(UGError("Error in InterfaceHandlerLocalDiffusion::update(): switch(m_elemModus)!")); + } + + + std::vector > vCornerCoords; + CollectCornerCoordinates(vCornerCoords, *elem, m_aaPos); + + std::vector ind; + dd->dof_indices(elem, 0, ind); + + // loop vertices + for(size_t i = 0; i < elem->num_vertices(); ++i) + { + // get vertex + // Vertex* vrt = elem->vertex(i); + + if (do_adjust) DoFRef(u, ind[i]) = 0.0; + } + + } + +} + +template +const size_t MovingInterface2PF:: +initialize_interface(vector_type& u, ConstSmartPtr dd) +{ + typedef typename domain_traits::grid_base_object grid_base_object; + + typename DoFDistribution::traits::const_iterator iter, iterEnd; + iter = dd->template begin(); + iterEnd = dd->template end(); + + size_t num_cutElements = 0; + + // loop elements in order to compute the volume and set rhs: + for( ; iter != iterEnd; iter++) + { + // get element + grid_base_object* elem = *iter; + + ElementModus elemModus = m_spCutElementHandler->compute_element_modus(elem); + + if ( elemModus == CUT_BY_INTERFACE ) + num_cutElements += 1; + + } + + return num_cutElements; + +} + +template +number MovingInterface2PF:: +MeanElementDiameter(TDomain& domain, int level) +{ + typedef typename domain_traits::grid_base_object TElem; + typedef typename geometry_traits::iterator ListIter; + + ListIter iter = domain.grid()->template begin(level); + ListIter iterEnd = domain.grid()->template end(level); + + number mean = 0.0; + size_t numIter = 0; + for (; iter != iterEnd; ++iter) { + mean += ElementDiameterSq(*domain.grid(), domain.position_accessor(), + *iter); + numIter++; + } + + mean = mean / numIter; + +#ifdef UG_PARALLEL + // share value between all procs + pcl::ProcessCommunicator com; + // ToDO: PCL_RO_MIN oder MAX oder doch mean noch berechnen m.H.v. /numProcs??? + //UG_THROW("in MeanElementDiameter(): was macht 'allredue' im Parallelen???\n"); + mean = com.allreduce(mean, PCL_RO_MIN); +#endif + + UG_LOG("nach com.allreduce: mean = " << std::sqrt(mean) << "\n"); + return std::sqrt(mean); +} + +template +void MovingInterface2PF:: +initialize_threshold(TDomain& domain, const int baseLevel, const int topLevel) +{ + UG_LOG("----------------- START initialize_threshold() ---------------- \n"); + + if (baseLevel < 0) + UG_THROW( + "initialize_threshold(): no cast of baselevel from 'int' tp 'size_t' possible! \n"); + if (topLevel < 0) + UG_THROW( + "initialize_threshold(): no cast of toplevel from 'int' tp 'size_t' possible! \n"); + +// compute level-dependent value for threshold: + for (size_t lev = baseLevel; lev <= (size_t) topLevel; ++lev) { + const number maxLength = MaxElementDiameter(domain, lev); + const number meanLength = MeanElementDiameter(domain, lev); + UG_LOG("maxLength = " << maxLength << "\n"); + UG_LOG("meanLength = " << meanLength << "\n"); + UG_LOG("threshold_max = " << maxLength*maxLength << "\n"); + UG_LOG("threshold_mean = " << meanLength*meanLength << "\n"); + + set_threshold(lev, meanLength * meanLength); + } + + UG_LOG("----------------- END initialize_threshold() ---------------- \n"); + +} + +template +void MovingInterface2PF:: +update_interface( const int topLevel, number deltaT) +{ + if ( deltaT == 0.0 ) + UG_THROW("ParticleProvider:update_prtCoords: deltaT = " << deltaT << " => no update necessary!\n"); + +// get level index + const int levIndex = get_Index(GridLevel(topLevel, GridLevel::LEVEL)); + +// update center + for (size_t p = 0; p < m_spInterfaceProvider->num_particles(); ++p) + { +#ifdef UG_PARALLEL + std::vector ElemList = m_spCutElementHandler->m_vvvElemListCut[levIndex][p]; + std::vector ElemList = m_vvvElemListCut[levIndex][p]; + if ( ElemList.size() == 0 ) + { continue; } +#endif + + // get data: + MathVector centerNew = m_spInterfaceProvider->get_center(p); + number soution = m_spInterfaceProvider->get_solution(p, 0); + + // ToDo: Hier das interface updaten: + + + } // end particle loop + +} + +} // end namespace NavierStokes +} // end namespace ug + +#endif /* PF2_INTERFACE_IMPL_ */ diff --git a/incompressible/incompressible_navier_stokes_plugin.cpp b/incompressible/incompressible_navier_stokes_plugin.cpp index 6a5ca30..68c5bc8 100644 --- a/incompressible/incompressible_navier_stokes_plugin.cpp +++ b/incompressible/incompressible_navier_stokes_plugin.cpp @@ -50,8 +50,7 @@ #include "fv/register_fv.h" #include "fvcr/register_fvcr.h" #include "fe/register_fe.h" - - +#include "particle_laden_flow_plugin.h" using namespace std; @@ -323,6 +322,8 @@ void Init___IncompressibleNavierStokes(Registry* reg, string grp) Init___NavierStokes___FVCR(reg, grp); Init___NavierStokes___FV(reg, grp); Init___NavierStokes___FE(reg, grp); + + Init___ParticleLadenFlow(reg, grp); } UG_REGISTRY_CATCH_THROW(grp); } diff --git a/incompressible/particle_laden_flow_plugin.cpp b/incompressible/particle_laden_flow_plugin.cpp new file mode 100644 index 0000000..2c78dcc --- /dev/null +++ b/incompressible/particle_laden_flow_plugin.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2011-2015: G-CSC, Goethe University Frankfurt + * Author: Sebastian Reiter + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#ifndef MOVING_PARTICLE_PLUGIN_CPP_ +#define MOVING_PARTICLE_PLUGIN_CPP_ + +#ifdef UG_PARALLEL + #include "../../Parmetis/src/unificator_interface.h" +#endif + +#include "bridge/util.h" +#include "bridge/util_domain_algebra_dependent.h" + +#include "common/log.h" +#include "lib_disc/function_spaces/grid_function.h" +#include "lib_disc/dof_manager/dof_distribution.h" +#include "fv1/navier_stokes_fv1_cutElem.h" +#include "incompressible_navier_stokes_base.h" +#include "fv1/bnd/inflow_fv1_cutElem.h" +#include "bnd/inflow_base.h" + + +#include "lib_disc/spatial_disc/local_to_global/local_to_global_mapper.h" +#include "lib_disc/spatial_disc/immersed_util/gmg_transfer/particle_transfer.h" +#include "fv1/moving_particle/moving_particle.h" +//#include "fv1/two_phase_flow/two_phase_flow.h" + + +using namespace std; +using namespace ug::bridge; + +namespace ug{ +namespace NavierStokes{ + +/** + * Class exporting the functionality of the plugin. All functionality that is to + * be used in scripts or visualization must be registered here. + */ +struct FunctionalityIncompMoving +{ + +/** + * Function called for the registration of Domain and Algebra dependent parts + * of the plugin. All Functions and Classes depending on both Domain and Algebra + * are to be placed here when registering. The method is called for all + * available Domain and Algebra types, based on the current build options. + * + * @param reg registry + * @param parentGroup group for sorting of functionality + */ +template +static void DomainAlgebra(Registry& reg, string grp) +{ + string suffix = GetDomainAlgebraSuffix(); + string tag = GetDomainAlgebraTag(); + + typedef ApproximationSpace approximation_space_type; + typedef GridFunction function_type; + +// IImmersedInterfaceBase + { + typedef IImmersedInterface T; + string name = string("IImmersedInterface").append(suffix); + reg.add_class_(name, grp); + reg.add_class_to_group(name, "IImmersedInterface", tag); + + } + + + +// MovingParticle + { + typedef MovingParticle T; + typedef IImmersedInterface TBase; + string name = string("MovingParticle").append(suffix); + reg.add_class_(name, grp) + .template add_constructor > ass, + SmartPtr > spMaster, + SmartPtr > cutElementHandler, + number fluidDensity, number fluidKinVisc)>("domain disc, global handler") + .add_method("init", &T::init) + .add_method("print_velocity", &T::print_velocity) + .add_method("print_deltaP", &T::print_deltaP) + .add_method("print_pressure_nodal", &T::print_pressure_nodal) + .add_method("print_pressure_teta", &T::print_pressure_teta) + .add_method("update", &T::update) + .add_method("get_velocity", &T::get_velocity) + .add_method("adjust_global_solution", &T::adjust_global_solution) + .add_method("compute_gradient_local_max", &T::compute_gradient_local_max) + .add_method("set_gravity", &T::set_gravity) + .add_method("set_time_step", &T::set_time_step) + .add_method("set_volume_comp_mode", &T::set_volume_comp_mode) + .add_method("set_StdFV_assembling", &T::set_StdFV_assembling) + .add_method("set_print_cutElemData", &T::set_print_cutElemData) + .add_method("initialize_threshold", &T::initialize_threshold) + .add_method("set_threshold", &T::set_threshold) + .add_method("get_BndCond", &T::get_BndCond) + .add_method("get_numCutElements", &T::get_numCutElements) + // methods for parallel many-particle simulations: +#ifdef UG_PARALLEL + .add_method("pre_balancing_update", &T::pre_balancing_update) + .add_method("post_balancing_update", &T::post_balancing_update) +#endif + .add_method("set_repulsive_force", &T::set_repulsive_force) + .add_method("set_glowinski_repulsive_force", &T::set_glowinski_repulsive_force) + .add_method("set_minimum_correction_force", &T::set_minimum_correction_force) + .add_method("set_element_diameter", &T::set_element_diameter) + .add_method("MeanElementDiameter", &T::MeanElementDiameter) + .add_method("set_forceLog", &T::set_forceLog) + .add_method("set_mpi_routine", &T::set_mpi_routine) + .add_method("estimate_repulsive_force_parameters", &T::estimate_repulsive_force_parameters) + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "MovingParticle", tag); + } + + // ParticleTransfer + { + typedef ParticleTransfer T; + typedef ITransferOperator TBase; + string name = string("ParticleTransfer").append(suffix); + reg.add_class_(name, grp) + .template add_constructor > approxSpace, SmartPtr > cutElementHandler)>("approxSpace, globalHandler") + .add_method("set_debug", &T::set_debug, "", "") + .add_method("set_use_transposed", &T::set_use_transposed, "", "") + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "ParticleTransfer", tag); + } + +} + +/** + * Function called for the registration of Algebra dependent parts. + * All Functions and Classes depending on Algebra + * are to be placed here when registering. The method is called for all + * available Algebra types, based on the current build options. + * + * @param reg registry + * @param parentGroup group for sorting of functionality + */ +template +static void Algebra(Registry& reg, string grp) +{ + string suffix = GetAlgebraSuffix(); + string tag = GetAlgebraTag(); +} + +/** + * Function called for the registration of Domain dependent parts + * of the plugin. All Functions and Classes depending on the Domain + * are to be placed here when registering. The method is called for all + * available Domain types, based on the current build options. + * + * @param reg registry + * @param parentGroup group for sorting of functionality + */ +template +static void Domain(Registry& reg, string grp) +{ + string suffix = GetDomainSuffix(); + string tag = GetDomainTag(); + + // ParticleBndCond + { + typedef ParticleBndCond T; + typedef IElemDisc TBase; + string name = string("ParticleBndCond").append(suffix); + reg.add_class_(name, grp), + reg.add_class_to_group(name, "ParticleBndCond", tag); + } +#ifdef UG_PARALLEL + // ParticleUnificator + { + typedef ParticleUnificator T; + typedef typename GeomObjBaseTypeByDim::base_obj_type TBaseElem; + typedef parmetis::IUnificator TBase; + string name = string("ParticleUnificator").append(suffix); + reg.add_class_(name, grp) + .template add_constructor)>("") + .add_method("update_particles", &T::update_particles) + .add_method("rebalance", &T::rebalance) + .set_construct_as_smart_pointer(true); + // .add_method("get_weight", &T::get_weight); + // .add_method("reweight", &T::reweight); + reg.add_class_to_group(name, "ParticleUnificator", tag); + } +#endif + +} + +/** + * Function called for the registration of Dimension dependent parts + * of the plugin. All Functions and Classes depending on the Dimension + * are to be placed here when registering. The method is called for all + * available Dimension types, based on the current build options. + * + * @param reg registry + * @param parentGroup group for sorting of functionality + */ +template +static void Dimension(Registry& reg, string grp) +{ + string suffix = GetDimensionSuffix(); + string tag = GetDimensionTag(); + + // CutElementHandler_FlatTop + { + typedef CutElementHandler_FlatTop T; + string name = string("CutElementHandler_FlatTop").append(suffix); + reg.add_class_(name, grp) + .template add_constructor mg, const char*, SmartPtr >)>("multigrid, fct names") + .template add_constructor mg, const char*, SmartPtr >)>("multigrid, fct names") + // .add_method("update_prtCoords", &T::update_prtCoords) + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "CutElementHandler_FlatTop", tag); + } + + + // ParticleProvider + { + typedef ParticleProvider T; + string name = string("ParticleProvider").append(suffix); + reg.add_class_(name, grp) + .template add_constructor("") + .add_method("print", &T::print) + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "ParticleProvider", tag); + } + + + // ParticleProviderSphere + { + typedef ParticleProviderSphere T; + string name = string("ParticleProviderSphere").append(suffix); + reg.add_class_(name, grp) + .template add_constructor("") + .add_method("print", &T::print) + .add_method("add", &T::add) + .add_method("add_moving", &T::add_moving) +// .add_method("get_collision_time", &T::get_collision_time) + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "ParticleProviderSphere", tag); + } + + // ParticleProviderEllipse + { + typedef ParticleProviderEllipse T; + string name = string("ParticleProviderEllipse").append(suffix); + reg.add_class_(name, grp) + .template add_constructor("") + .add_method("print", &T::print) + .add_method("add", &T::add) + .add_method("add_moving", &T::add_moving) + .set_construct_as_smart_pointer(true); + reg.add_class_to_group(name, "ParticleProviderEllipse", tag); + } + + +} + + +static void Common(Registry& reg, string grp) +{ + // write numbers into file + /* TODO: obsolete / replace by lua code */ + { + //reg.add_function("writeNumbers", static_cast(&writeNumbers), grp); + } + // write numbers into file + /* TODO: obsolete / replace by lua code */ + { + //reg.add_function("clearFile", static_cast(&clearFile), grp); + } +} + +}; // end Functionality +} // end namespace NavierStokes + + +/** + * This function is called when the plugin is loaded. + */ +void Init___ParticleLadenFlow(Registry* reg, string grp) +{ + grp.append("SpatialDisc/NavierStokes/"); + typedef NavierStokes::FunctionalityIncompMoving Functionality; + + try{ + RegisterDimension2d3dDependent(*reg,grp); + RegisterDomain2d3dDependent(*reg,grp); + RegisterDomain2d3dAlgebraDependent(*reg,grp); + } + UG_REGISTRY_CATCH_THROW(grp); +} + + + +}// namespace ug + + +#endif /* MOVING_PARTICLE_PLUGIN_CPP_ */ diff --git a/incompressible/particle_laden_flow_plugin.h b/incompressible/particle_laden_flow_plugin.h new file mode 100644 index 0000000..5e909db --- /dev/null +++ b/incompressible/particle_laden_flow_plugin.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015: G-CSC, Goethe University Frankfurt + * Author: Andreas Vogel + * + * This file is part of UG4. + * + * UG4 is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License version 3 (as published by the + * Free Software Foundation) with the following additional attribution + * requirements (according to LGPL/GPL v3 §7): + * + * (1) The following notice must be displayed in the Appropriate Legal Notices + * of covered and combined works: "Based on UG4 (www.ug4.org/license)". + * + * (2) The following notice must be displayed at a prominent place in the + * terminal output of covered works: "Based on UG4 (www.ug4.org/license)". + * + * (3) The following bibliography is recommended for citation and must be + * preserved in all covered files: + * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively + * parallel geometric multigrid solver on hierarchically distributed grids. + * Computing and visualization in science 16, 4 (2013), 151-164" + * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel + * flexible software system for simulating pde based models on high performance + * computers. Computing and visualization in science 16, 4 (2013), 165-179" + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ + +#ifndef MOVING_PARTICLE_PLUGIN_H_ +#define MOVING_PARTICLE_PLUGIN_H_ + + +namespace ug{ + +#include "bridge/util.h" +#include + + void + Init___ParticleLadenFlow(ug::bridge::Registry* reg, std::string grp); + + +} // end namespace ug + +#endif /* MOVING_PARTICLE_PLUGIN_H_ */ diff --git a/register_navier_stokes.h b/register_navier_stokes.h index fd4969e..8d16618 100644 --- a/register_navier_stokes.h +++ b/register_navier_stokes.h @@ -40,6 +40,7 @@ namespace ug{ void Init___CompressibleNavierStokes(bridge::Registry* reg, std::string grp); void Init___IncompressibleNavierStokes(bridge::Registry* reg, std::string grp); +void Init___ParticleLadenFlow(bridge::Registry* reg, std::string grp); }// namespace ug diff --git a/upwind.cpp b/upwind.cpp index 708b14a..4ebf7d4 100644 --- a/upwind.cpp +++ b/upwind.cpp @@ -126,10 +126,85 @@ compute(const HCRFVGeometry* geo, } } } + + + template + template +void +NavierStokesNoUpwind:: +compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]) +{ + // set shapes + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // get SubControlVolumeFace + const typename DimFV1FTGeometry >::SCVF& scvf = geo->scvf(ip); + + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + { + // set upwind shape + vUpShapeSh[ip][sh] = scvf.shape(sh); + } + // compute convection length + // \todo: (optional) A convection length is not really defined for no upwind. + // but in the computation of a stabilization the term cancels, so + // we only have to ensure that the conv_lengh is non-zero + vConvLength[ip] = 1.0; + + } + +} + ///////////////////////////////////////////////////////////////////////////// // Full Upwind ///////////////////////////////////////////////////////////////////////////// + +template +template +void +NavierStokesFullUpwind:: +compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]) +{ + // two help vectors + MathVector dist; + + // get corners of elem + const MathVector* corners = geo->corners(); + + // set shapes + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // get SubControlVolumeFace + const typename DimFV1FTGeometry >::SCVF& scvf = geo->scvf(ip); + + // reset shapes to zero for all IPs + for (size_t sh = 0; sh < scvf.num_sh(); ++sh) + vUpShapeSh[ip][sh]=0.0; + + // switch upwind + const number flux = VecDot(scvf.normal(), vIPVel[ip]); + if(flux > 0.0) + { + vUpShapeSh[ip][scvf.from()] = 1.0; + vConvLength[ip] = VecDistance(scvf.global_ip(), corners[scvf.from()]); + } + else + { + vUpShapeSh[ip][scvf.to()] = 1.0; + vConvLength[ip] = VecDistance(scvf.global_ip(), corners[scvf.to()]); + } + } +} + template template void @@ -378,6 +453,7 @@ void GetNodeNextToCut(size_t& coOut, } } + template template void @@ -388,47 +464,47 @@ compute(const FV1Geometry* geo, number vUpShapeIp[maxNumSCVF][maxNumSCVF], number vConvLength[maxNumSCVF]) { -// corners of geometry - const MathVector* vCornerCoords = geo->corners(); - -// loop all scvf - for(size_t ip = 0; ip < geo->num_scvf(); ++ip) - { - // get SubControlVolumeFace - const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); - - // reset shapes to zero - for(size_t sh = 0; sh < scvf.num_sh(); ++sh) - vUpShapeSh[ip][sh] = 0.0; - - // if the velocity is zero, there will be no possibility to find the - // cutted side. In this case we have no velocity and therefore there is - // no convection. We set all upwind shapes to zero. - if(VecTwoNorm(vIPVel[ip]) < 1e-14) { - // \todo: (optional) A convection length is not really defined. - // but in the computation of a stabilization the term cancels, so - // we only have to ensure that the conv_lengh is non-zero - vConvLength[ip] = 1.0; - continue; - } - - // upwind corner - size_t sh = 0; - - // find upwind node - try{ - GetNodeNextToCut::ref_elem_type, dim> - (sh, scvf.global_ip(), vIPVel[ip], vCornerCoords); - }UG_CATCH_THROW("GetSkewedUpwindShapes: Cannot find upwind node."); - - // set upwind corner - vUpShapeSh[ip][sh] = 1.0; - - // compute convection length - vConvLength[ip] = VecDistance(scvf.global_ip(), vCornerCoords[sh]); - } + // corners of geometry + const MathVector* vCornerCoords = geo->corners(); + + // loop all scvf + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // get SubControlVolumeFace + const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + + // reset shapes to zero + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + vUpShapeSh[ip][sh] = 0.0; + + // if the velocity is zero, there will be no possibility to find the + // cutted side. In this case we have no velocity and therefore there is + // no convection. We set all upwind shapes to zero. + if(VecTwoNorm(vIPVel[ip]) < 1e-14) { + // \todo: (optional) A convection length is not really defined. + // but in the computation of a stabilization the term cancels, so + // we only have to ensure that the conv_lengh is non-zero + vConvLength[ip] = 1.0; + continue; + } + + // upwind corner + size_t sh = 0; + + // find upwind node + try{ + GetNodeNextToCut::ref_elem_type, dim> + (sh, scvf.global_ip(), vIPVel[ip], vCornerCoords); + }UG_CATCH_THROW("GetSkewedUpwindShapes: Cannot find upwind node."); + + // set upwind corner + vUpShapeSh[ip][sh] = 1.0; + + // compute convection length + vConvLength[ip] = VecDistance(scvf.global_ip(), vCornerCoords[sh]); + } } - + template template void @@ -501,7 +577,6 @@ compute(const CRFVGeometry* geo, ///////////////////////////////////////////////////////////////////////////// // Linear Profile Skewed Upwind ///////////////////////////////////////////////////////////////////////////// - template template void @@ -511,6 +586,177 @@ compute(const FV1Geometry* geo, number vUpShapeSh[maxNumSCVF][maxNumSH], number vUpShapeIp[maxNumSCVF][maxNumSCVF], number vConvLength[maxNumSCVF]) +{ + // corners of geometry + const MathVector* vCornerCoords = geo->corners(); + + // loop all scvf + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // get SubControlVolumeFace + const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); + + // reset shapes to zero + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + vUpShapeSh[ip][sh] = 0.0; + + // if the velocity is zero, there will be no possibility to find the + // cutted side. In this case we have no velocity and therefore there is + // no convection. We set all upwind shapes to zero. + if(VecTwoNorm(vIPVel[ip]) < 1e-14) { + // \todo: (optional) A convection length is not really defined. + // but in the computation of a stabilization the term cancels, so + // we only have to ensure that the conv_lengh is non-zero + vConvLength[ip] = 1.0; + continue; + } + + // side and intersection vectors + static const int refDim = FV1Geometry::dim; + size_t side = 0; + MathVector globalIntersection; + MathVector localIntersection; + + // find local intersection and side + try{ + ElementSideRayIntersection::ref_elem_type, dim> + ( side, globalIntersection, localIntersection, + scvf.global_ip(), vIPVel[ip], false /* search upwind */, vCornerCoords); + }UG_CATCH_THROW("GetLinearProfileSkewedUpwindShapes: Cannot find cut side."); + + // get linear trial space + static const ReferenceObjectID roid = reference_element_traits::reference_element_type::REFERENCE_OBJECT_ID; + const LocalShapeFunctionSet& TrialSpace = + LocalFiniteElementProvider::get(roid, LFEID(LFEID::LAGRANGE, dim, 1)); + + // get Reference Element + typedef typename FV1Geometry::ref_elem_type ref_elem_type; + static const ref_elem_type& rRefElem + = Provider::get(); + + // loop corners of side + for(size_t j = 0; j < rRefElem.num(dim-1, side, 0); ++j) + { + // get corner + const size_t co = rRefElem.id(dim-1, side, 0, j); + + // evaluate trial space + vUpShapeSh[ip][co] = TrialSpace.shape(co, localIntersection); + } + + // compute conv length + vConvLength[ip] = VecDistance(scvf.global_ip(), globalIntersection); + } +} + +////////////////////////////////////////////////////////////////////////////////// +// +// DimWrapper_ElementSideRayIntersection(): +// +// Wrapper functions in order to call the method 'ElementSideRayIntersection' +// for different dimensions and reference element types. +// For the 'DimFV1FTGeometry' the reference element type can change, therefore +// a different call of the method 'ElementSideRayIntersection' is necessary +// for call within 'NavierStokesLinearProfileSkewedUpwind()' +// +////////////////////////////////////////////////////////////////////////////////// + + +template +inline void DimWrapper_ElementSideRayIntersection(const ReferenceObjectID roid, size_t& sideOut, + MathVector& GlobalIntersectionPointOut, + MathVector& LocalIntersectionPoint, + const MathVector& From, const MathVector& Direction, + bool bPositiv, const MathVector* vCornerCoords); + +template <> +inline void DimWrapper_ElementSideRayIntersection<1>(const ReferenceObjectID roid, size_t& sideOut, + MathVector<1>& GlobalIntersectionPointOut, + MathVector<1>& LocalIntersectionPoint, + const MathVector<1>& From, const MathVector<1>& Direction, + bool bPositiv, const MathVector<1>* vCornerCoords) +{ + UG_THROW("in DimWrapper_ElementSideRayIntersection<1>: not implemented for 1d!\n") +} + +template <> +inline void DimWrapper_ElementSideRayIntersection<2>(const ReferenceObjectID roid, size_t& sideOut, + MathVector<2>& GlobalIntersectionPointOut, + MathVector<2>& LocalIntersectionPoint, + const MathVector<2>& From, const MathVector<2>& Direction, + bool bPositiv, const MathVector<2>* vCornerCoords) +{ + switch(roid) + { + case ROID_TRIANGLE: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 2, 2>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<2>: Cannot find cut side."); + break; + case ROID_QUADRILATERAL: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 2, 2>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<2>: Cannot find cut side."); + break; + default: UG_THROW("Reference Element type for roid "< +inline void DimWrapper_ElementSideRayIntersection<3>(const ReferenceObjectID roid, size_t& sideOut, + MathVector<3>& GlobalIntersectionPointOut, + MathVector<3>& LocalIntersectionPoint, + const MathVector<3>& From, const MathVector<3>& Direction, + bool bPositiv, const MathVector<3>* vCornerCoords) +{ + switch(roid) + { + case ROID_TETRAHEDRON: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 3, 3>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<3> - ROID_TETRAHEDRON: Cannot find cut side."); + break; + case ROID_PYRAMID: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 3, 3>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<3> - ROID_PYRAMID: Cannot find cut side."); + break; + case ROID_PRISM: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 3, 3>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<3> - ROID_PRISM: Cannot find cut side."); + break; + case ROID_HEXAHEDRON: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 3, 3>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<3> - ROID_HEXAHEDRON: Cannot find cut side."); + break; + case ROID_OCTAHEDRON: try{ + ElementSideRayIntersectionWrapper::reference_element_type, 3, 3>:: + apply(sideOut, GlobalIntersectionPointOut, LocalIntersectionPoint, From, Direction, + bPositiv, vCornerCoords); + }UG_CATCH_THROW("DimWrapper_ElementSideRayIntersection<3> - ROID_OCTAHEDRON: Cannot find cut side."); + break; + default: UG_THROW("Reference Element type for roid "< +template +void +NavierStokesLinearProfileSkewedUpwind:: +compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]) { // corners of geometry const MathVector* vCornerCoords = geo->corners(); @@ -519,8 +765,9 @@ compute(const FV1Geometry* geo, for(size_t ip = 0; ip < geo->num_scvf(); ++ip) { // get SubControlVolumeFace - const typename FV1Geometry::SCVF& scvf = geo->scvf(ip); - + typedef DimFV1FTGeometry > TFVGeom; + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + // reset shapes to zero for(size_t sh = 0; sh < scvf.num_sh(); ++sh) vUpShapeSh[ip][sh] = 0.0; @@ -536,29 +783,36 @@ compute(const FV1Geometry* geo, continue; } + if ((vIPVel[ip][0] != vIPVel[ip][0]) || (vIPVel[ip][1] != vIPVel[ip][1]) || (vIPVel[ip][2] != vIPVel[ip][2])) { + vConvLength[ip] = 1.0; + continue; + } + // side and intersection vectors - static const int refDim = FV1Geometry::dim; + static const int refDim = TFVGeom::dim; size_t side = 0; MathVector globalIntersection; - MathVector localIntersection; +// MathVector localIntersection; + MathVector localIntersection; + // get reference object ID + const ReferenceObjectID roid = geo->get_roid(); + // find local intersection and side try{ - ElementSideRayIntersection::ref_elem_type, dim> - ( side, globalIntersection, localIntersection, - scvf.global_ip(), vIPVel[ip], false /* search upwind */, vCornerCoords); - }UG_CATCH_THROW("GetLinearProfileSkewedUpwindShapes: Cannot find cut side."); + DimWrapper_ElementSideRayIntersection + ( roid, side, globalIntersection, localIntersection, + scvf.global_ip(), vIPVel[ip], false , vCornerCoords); + }UG_CATCH_THROW("GetLinearProfileSkewedUpwindShapes: Cannot find cut side."); // get linear trial space - static const ReferenceObjectID roid = reference_element_traits::reference_element_type::REFERENCE_OBJECT_ID; - const LocalShapeFunctionSet& TrialSpace = + const LocalShapeFunctionSet& TrialSpace = LocalFiniteElementProvider::get(roid, LFEID(LFEID::LAGRANGE, dim, 1)); // get Reference Element - typedef typename FV1Geometry::ref_elem_type ref_elem_type; - static const ref_elem_type& rRefElem - = Provider::get(); + const DimReferenceElement& rRefElem = ReferenceElementProvider::get(roid); + // loop corners of side for(size_t j = 0; j < rRefElem.num(dim-1, side, 0); ++j) { @@ -639,6 +893,154 @@ compute(const CRFVGeometry* geo, ///////////////////////////////////////////////////////////////////////////// // Positive Upwind ///////////////////////////////////////////////////////////////////////////// + +template +template +void +NavierStokesPositiveUpwind:: +compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]) +{ + + // 1. Reset values and compute ip velocities and Compute mass fluxes at ip's + + // vector for flux values + std::vector vMassFlux(geo->num_scvf(), 0.0); + std::vector vHasFlux(geo->num_scvf(), true); + + typedef DimFV1FTGeometry > TFVGeom; + + // loop all scvf + const number eps = std::numeric_limits::epsilon() * 10; + size_t bNumNoFlux = 0; + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // get SubControlVolumeFace + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + + // reset shapes w.r.t corner value to zero + for(size_t sh = 0; sh < scvf.num_sh(); ++sh) + vUpShapeSh[ip][sh] = 0.0; + + // reset shapes w.r.t. ip value to zero and extract ip vel + for(size_t j = 0; j < geo->num_scvf(); ++j) + vUpShapeIp[ip][j] = 0.0; + + const number normSq = VecTwoNormSq(vIPVel[ip]); + if(fabs(normSq) <= eps) + { + vUpShapeSh[ip][scvf.from()] = 0.5; + vUpShapeSh[ip][scvf.to()] = 0.5; + vHasFlux[ip] = false; + bNumNoFlux++; + continue; + } + + vMassFlux[ip] = VecProd(vIPVel[ip], scvf.normal()); + const number vel = std::sqrt(normSq); + const number len = VecTwoNorm(scvf.normal()); + if(fabs(vMassFlux[ip] / std::sqrt(vel*len)) <= eps) + { + vUpShapeSh[ip][scvf.from()] = 0.5; + vUpShapeSh[ip][scvf.to()] = 0.5; + vHasFlux[ip] = false; + bNumNoFlux++; + continue; + } + } + + + // 2. Handle each SCV separately + if(bNumNoFlux != geo->num_scvf()) + { + for(size_t sh = 0; sh < geo->num_sh(); ++sh) + { + // reset inflow, outflow + number m_in = 0, m_out = 0; + std::vector vSCVIP; + std::vector vFlux; + + // loop subcontrol volume faces + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // if no flux skip + if(!vHasFlux[ip]) continue; + + // get SubControlVolumeFace + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + + // if scvf is part of the scv, add fluxes + if(scvf.from() == sh) + { + // normal directed outwards + vSCVIP.push_back(ip); + vFlux.push_back( vMassFlux[ip] ); + m_in += -1.0 * std::min(vMassFlux[ip], 0.0); + m_out += std::max(vMassFlux[ip], 0.0); + } + else if (scvf.to() == sh) + { + // normal directed inwards + vSCVIP.push_back(ip); + vFlux.push_back( -1.0 * vMassFlux[ip] ); + m_in += -1.0 * std::min(-1.0 * vMassFlux[ip], 0.0); + m_out += std::max(-1.0 * vMassFlux[ip], 0.0); + } + } + + // compute F + number F = std::max(m_in, m_out); + + // set shapes + for(size_t i = 0; i < vSCVIP.size(); ++i) + { + if(vFlux[i] > 0) + { + number sum = 0.0; + for(size_t j = 0; j < vSCVIP.size(); ++j) + { + if(vFlux[j] < 0) + { + // set ip shapes + sum += vUpShapeIp[vSCVIP[i]][vSCVIP[j]] = -1.0 * vFlux[j] / F; + } + } + // set nodal shapes + vUpShapeSh[vSCVIP[i]][sh] = 1.0 - sum; + } + } + } + } + + // 3. compute convection length + + // corners of geometry + const MathVector* vCornerCoords = geo->corners(); + + // compute upwind point + MathVector upPos; + for(size_t ip = 0; ip < geo->num_scvf(); ++ip) + { + // get SubControlVolumeFace + const typename TFVGeom::SCVF& scvf = geo->scvf(ip); + + // reset upwind point + VecSet(upPos, 0.0); + + // sum up contributions + for (size_t sh = 0; sh < scvf.num_sh(); ++sh) + VecScaleAppend(upPos, vUpShapeSh[ip][sh], vCornerCoords[sh]); + for (size_t j = 0; j < geo->num_scvf(); ++j) + VecScaleAppend(upPos, vUpShapeIp[ip][j], geo->scvf(j).global_ip()); + + // save convection length + vConvLength[ip] = VecDistance(scvf.global_ip(), upPos); + } +} + template template diff --git a/upwind.h b/upwind.h index 49f43b1..bfbeb1a 100644 --- a/upwind.h +++ b/upwind.h @@ -44,6 +44,7 @@ #include "lib_disc/spatial_disc/disc_util/fv1_geom.h" #include "lib_disc/spatial_disc/disc_util/fvcr_geom.h" #include "lib_disc/spatial_disc/disc_util/hfvcr_geom.h" +#include "lib_disc/spatial_disc/disc_util/fv1FT_geom.h" namespace ug{ namespace NavierStokes{ @@ -57,7 +58,8 @@ class NavierStokesNoUpwind : public INavierStokesUpwind, public NavierStokesUpwindRegister >, public NavierStokesUpwindRegister >, - public NavierStokesUpwindRegister > + public NavierStokesUpwindRegister >, + public NavierStokesUpwindRegisterDim >, dim, NavierStokesNoUpwind > { public: typedef INavierStokesUpwind base_type; @@ -91,6 +93,14 @@ class NavierStokesNoUpwind number vUpShapeSh[maxNumSCVF][maxNumSH], number vUpShapeIp[maxNumSCVF][maxNumSCVF], number vConvLength[maxNumSCVF]); + + /// update of values for DimFV1FTGeometry + template + static void compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]); }; ///////////////////////////////////////////////////////////////////////////// @@ -102,7 +112,8 @@ class NavierStokesFullUpwind : public INavierStokesUpwind, public NavierStokesUpwindRegister >, public NavierStokesUpwindRegister >, - public NavierStokesUpwindRegister > + public NavierStokesUpwindRegister >, + public NavierStokesUpwindRegisterDim >, dim, NavierStokesFullUpwind > { public: typedef INavierStokesUpwind base_type; @@ -136,6 +147,15 @@ class NavierStokesFullUpwind number vUpShapeSh[maxNumSCVF][maxNumSH], number vUpShapeIp[maxNumSCVF][maxNumSCVF], number vConvLength[maxNumSCVF]); + + /// update of values for DimFV1FTGeometry + template + static void compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]); + }; @@ -220,7 +240,8 @@ template class NavierStokesLinearProfileSkewedUpwind : public INavierStokesUpwind, public NavierStokesUpwindRegister >, - public NavierStokesUpwindRegister > + public NavierStokesUpwindRegister >, + public NavierStokesUpwindRegisterDim >, dim, NavierStokesLinearProfileSkewedUpwind > { public: typedef INavierStokesUpwind base_type; @@ -246,6 +267,15 @@ class NavierStokesLinearProfileSkewedUpwind number vUpShapeSh[maxNumSCVF][maxNumSH], number vUpShapeIp[maxNumSCVF][maxNumSCVF], number vConvLength[maxNumSCVF]); + + /// update of values for DimFV1FTGeometry + template + static void compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]); + }; @@ -256,7 +286,8 @@ class NavierStokesLinearProfileSkewedUpwind template class NavierStokesPositiveUpwind : public INavierStokesUpwind, - public NavierStokesUpwindRegister > + public NavierStokesUpwindRegister >, + public NavierStokesUpwindRegisterDim >,dim, NavierStokesPositiveUpwind > { public: typedef INavierStokesUpwind base_type; @@ -274,6 +305,16 @@ class NavierStokesPositiveUpwind number vUpShapeSh[maxNumSCVF][maxNumSH], number vUpShapeIp[maxNumSCVF][maxNumSCVF], number vConvLength[maxNumSCVF]); + + /// update of values for DimFV1FTGeometry + template + static void compute(const DimFV1FTGeometry >* geo, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]); + + }; ///////////////////////////////////////////////////////////////////////////// diff --git a/upwind_interface.h b/upwind_interface.h index 16869e4..d21f6f9 100644 --- a/upwind_interface.h +++ b/upwind_interface.h @@ -88,14 +88,14 @@ class INavierStokesUpwind /// Convection Length number upwind_conv_length(size_t scvf) const { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + // UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); return m_vUpConvLength[scvf]; } /// Convection Length number downwind_conv_length(size_t scvf) const { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + // UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); return m_vDownConvLength[scvf]; } @@ -107,16 +107,18 @@ class INavierStokesUpwind /// upwind shape for corner vel number upwind_shape_sh(size_t scvf, size_t sh) const { - // UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - // UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); - return m_vvUpShapeSh[scvf][sh]; + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); + + return m_vvUpShapeSh[scvf][sh]; } /// upwind shape for corner vel number downwind_shape_sh(size_t scvf, size_t sh) const { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); + return m_vvDownShapeSh[scvf][sh]; } @@ -126,16 +128,18 @@ class INavierStokesUpwind /// upwind shapes for ip vel number upwind_shape_ip(size_t scvf, size_t scvf2) const { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index: " << scvf); - UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index2: " << scvf2); + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index: " << scvf); + //UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index2: " << scvf2); + return m_vvUpShapeIp[scvf][scvf2]; } /// upwind shapes for ip vel number downwind_shape_ip(size_t scvf, size_t scvf2) const { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index"); + return m_vvDownShapeIp[scvf][scvf2]; } @@ -175,46 +179,50 @@ class INavierStokesUpwind /// non-const access to upwind shapes for corner vel number& upwind_shape_sh(size_t scvf, size_t sh) { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); + return m_vvUpShapeSh[scvf][sh]; } /// non-const access to upwind shapes for corner vel number& downwind_shape_sh(size_t scvf, size_t sh) { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); - return m_vvDownShapeSh[scvf][sh]; + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(sh < m_numSh, "Invalid index"); + + return m_vvDownShapeSh[scvf][sh]; } /// non-const access to upwind shapes for ip vel number& upwind_shape_ip(size_t scvf, size_t scvf2) { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index"); - return m_vvUpShapeIp[scvf][scvf2]; + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index"); + + return m_vvUpShapeIp[scvf][scvf2]; } /// non-const access to upwind shapes for ip vel number& downwind_shape_ip(size_t scvf, size_t scvf2) { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); - UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index"); - return m_vvDownShapeIp[scvf][scvf2]; + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf2 < m_numScvf, "Invalid index"); + + return m_vvDownShapeIp[scvf][scvf2]; } /// non-const access to Convection Length number& upwind_conv_length(size_t scvf) { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); return m_vUpConvLength[scvf]; } /// non-const access to Convection Length number& down_upwind_conv_length(size_t scvf) { - UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); + //UG_NSUPWIND_ASSERT(scvf < m_numScvf, "Invalid index"); return m_vDownConvLength[scvf]; } @@ -321,9 +329,19 @@ set_geometry_type() m_id = id; // set sizes - static TFVGeom& geo = GeomProvider::get(); - m_numScvf = geo.num_scvf(); - m_numSh = geo.num_sh(); + if (TFVGeom::staticLocalData) + { + TFVGeom& geo = GeomProvider::get(); + m_numScvf = geo.num_scvf(); + m_numSh = geo.num_sh(); + } + else // setting data later! + { + // hard code number != maxNumSCVF, since in compute_vel() if not: num_sh() = 4 > 3!! + m_numScvf = dim+2; // maxNumSCVF; + m_numSh = dim+2; // maxNumSH; + } + UG_NSUPWIND_ASSERT(m_numScvf <= maxNumSCVF, "Invalid index"); UG_NSUPWIND_ASSERT(m_numSh <= maxNumSH, "Invalid index"); @@ -412,6 +430,62 @@ class NavierStokesUpwindRegister TImpl& getImpl() {return static_cast(*this);} }; +template +class NavierStokesUpwindRegisterDim +{ +public: + /// Base class + typedef INavierStokesUpwind base_type; + +protected: + static const size_t maxNumSCVF = base_type::maxNumSCVF; + static const size_t maxNumSH = base_type::maxNumSH; + +public: + /// constructor + NavierStokesUpwindRegisterDim() {register_func(Int2Type());} + +private: + void register_func(Int2Type<1>) + { + register_func(); + } + + void register_func(Int2Type<2>) + { + register_func(); + register_func(); + } + + void register_func(Int2Type<3>) + { + register_func(); + register_func(); + register_func(); + register_func(); + } + + template + void register_func() + { + typedef TFVGeom TGeom; + // typedef void (TImpl::*TFunc)( + typedef void (*TFunc)( + const TGeom* obj, + const MathVector vIPVel[maxNumSCVF], + number vUpShapeSh[maxNumSCVF][maxNumSH], + number vUpShapeIp[maxNumSCVF][maxNumSCVF], + number vConvLength[maxNumSCVF]); + + getImpl().template register_update_func(&TImpl::template compute); + } + +protected: + /// access to implementation + TImpl& getImpl() {return static_cast(*this);} +}; + + /// creates upwind based on a string identifier template SmartPtr > CreateNavierStokesUpwind(const std::string& name);