Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/penn-graphics-research/ziran2019
11 June 2024, 08:44:58 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/master
    No releases to show
  • 31424c6
  • /
  • Projects
  • /
  • admm
  • /
  • AdmmSimulationFoam.h
Raw File Download
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:1cff7ba9a0c408d71110d06aa0f0818c740654c2
origin badgedirectory badge
swh:1:dir:7865fa2847d587f5f91e883ece58e7da0c035233
origin badgerevision badge
swh:1:rev:35742ac3ab1ae42cf2bbe8761fd7c8e630a638c4
origin badgesnapshot badge
swh:1:snp:597f10704966a63e22a6690fc91719ea5d363ec6

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: 35742ac3ab1ae42cf2bbe8761fd7c8e630a638c4 authored by JoshWolper on 28 October 2020, 15:53:00 UTC
Add files via upload
Tip revision: 35742ac
AdmmSimulationFoam.h
#ifndef ADMM_SIMULATION_FOAM_H
#define ADMM_SIMULATION_FOAM_H

#include <Ziran/CS/Util/AttributeNamesForward.h>
#include <Ziran/CS/Util/Forward.h>
#include <Ziran/Physics/ConstitutiveModel/StvkWithHencky.h>
#include <Ziran/Physics/PlasticityApplier.h>
#include <Ziran/Math/Linear/Minres.h>
#include <MPM/MpmSimulationBase.h>
#include <Eigen/LU>
#include <Partio.h>
#include <map>
#include <mutex>

#include "AdmmFormula.h"
#include "AdmmSketch.h"
#include "GlobalSystem.h"
//#include "ConjugateGradient.h"
#include <Ziran/Math/Linear/Minres.h>
#include <Ziran/Math/Geometry/CollisionObject.h>

namespace ZIRAN {

template <class T, int dim>
class AdmmSimulation : public MpmSimulationBase<T, dim> {
public:
    using Base = MpmSimulationBase<T, dim>;

    typedef Vector<T, dim> TV;
    typedef Vector<int, dim> IV;
    typedef Matrix<T, dim, dim> TM;
    typedef Vector<T, Eigen::Dynamic> TStack;
    typedef Matrix<T, dim, Eigen::Dynamic> TVStack;

    using Base::block_offset;
    using Base::collision_objects;
    using Base::dt;
    using Base::dv;
    using Base::dx;
    using Base::grid;
    using Base::mass_matrix;
    using Base::particle_base_offset;
    using Base::particle_group;
    using Base::particle_order;
    using Base::particles;
    using Base::step;
    using Base::vn;
    using typename Base::SparseMask;

    StdVector<TM>* Fn_pointer;
    StdVector<TM>* FFn_pointer;
    StdVector<TM> F;
    StdVector<TM> y;
    StdVector<T> omega;
    T rho_scale = 0.5;
    T over_relaxation_alpha = 1;

    using Simulation = AdmmSimulation<T, dim>;
    using Objective = GlobalSystem<Simulation, dim>;

    Objective cg_objective;
    Minres<T, Objective, TVStack> cg;

    T out_a = 0;
    int total_cg_iterations = 0;
    std::mutex mtx;

    bool use_ruiz = false;
    bool use_elasticity_plasticity = true;
    int admm_max_iterations = 100;
    T global_tolerance = 1e-6;
    T local_tolerance = 1e-8;
    int local_max_iteration;
    int global_max_iteration = 10000;

    static const bool build_matrix = true;

    TVStack preconditioner;
    std::vector<T> multiplier_value;
    std::vector<int> multiplier_idx;

    void visualize_particles(StdVector<TM>* pointer, std::string filename)
    {
        Partio::ParticlesDataMutable* parts = Partio::create();

        // visualize particles info
        Partio::ParticleAttribute posH, F00H, F01H, F10H, F11H;
        posH = parts->addAttribute("position", Partio::VECTOR, 3);
        F00H = parts->addAttribute("F00", Partio::VECTOR, 1);
        F01H = parts->addAttribute("F01", Partio::VECTOR, 1);
        F10H = parts->addAttribute("F10", Partio::VECTOR, 1);
        F11H = parts->addAttribute("F11", Partio::VECTOR, 1);

        for (int k = 0; k < particles.count; k++) {
            int idx = parts->addParticle();
            float* posP = parts->dataWrite<float>(posH, idx);
            float* F00P = parts->dataWrite<float>(F00H, idx);
            float* F01P = parts->dataWrite<float>(F01H, idx);
            float* F10P = parts->dataWrite<float>(F10H, idx);
            float* F11P = parts->dataWrite<float>(F11H, idx);
            for (int d = 0; d < 3; ++d) posP[d] = 0;
            for (int d = 0; d < dim; ++d) posP[d] = particles.X.array[k](d);
            F00P[0] = (*pointer)[k](0, 0);
            F01P[0] = (*pointer)[k](0, 1);
            F10P[0] = (*pointer)[k](1, 0);
            F11P[0] = (*pointer)[k](1, 1);
        }

        Partio::write(filename.c_str(), *parts);
        parts->release();
    }

    void writeState(std::ostream& out)
    {
        Base::writeState(out);
        return;
        {
            std::string filename = SimulationBase::output_dir.absolutePath(SimulationBase::outputFileName("F_e", ".bgeo"));
            visualize_particles(Fn_pointer, filename);
        }
        {
            std::string filename = SimulationBase::output_dir.absolutePath(SimulationBase::outputFileName("F_ne", ".bgeo"));
            visualize_particles(FFn_pointer, filename);
        }
    }

    AdmmSimulation()
        : Base(), cg(10000)
    {
        auto& Xarray = particles.X.array;
        auto ranges = particles.X.ranges;

        if constexpr (build_matrix) {
            cg_objective.setMultiplier([&](const TVStack& x, TVStack& b) {
                b.setZero();
                int block_size = (dim == 2 ? 25 : 125);
                grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
                    T* _value = &multiplier_value[g.idx * block_size];
                    int* _idx = &multiplier_idx[g.idx * block_size];
                    TV ret = g.m * x.col(g.idx);
                    for (int i = 0; i < block_size; ++i)
                        ret += x.col(_idx[i]) * _value[i];
                    b.col(g.idx) = ret;
                });
            });
            cg_objective.setPreconditioner([&](const TVStack& in, TVStack& out) {
                out = in.cwiseQuotient(preconditioner);
            });
        }
        else {
            ZIRAN_ASSERT(false, "moved into AdmmSketch.h");
        }
        cg_objective.setProjection([&](TVStack& r) {
            for (auto iter = Base::collision_nodes.begin(); iter != Base::collision_nodes.end(); ++iter) {
                int node_id = (*iter).node_id;
                TV tmp = r.col(node_id);
                (*iter).project(tmp);
                r.col(node_id) = tmp;
            }
        });
    }

    void dvStep(T tolerance, T relative_tolerance)
    {
        ZIRAN_TIMER();
        auto& Xarray = particles.X.array;

        TVStack rhs = TVStack::Zero(dim, Base::num_nodes);
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            rhs.col(g.idx) = dt * g.m * Base::gravity;
        });

        // G2P
        StdVector<TM> bFu(particles.count * 2, TM::Zero());
        tbb::parallel_for(0, (int)particles.count, [&](int i) {
            TV& Xp = Xarray[i];
            TM& Fn = (*Fn_pointer)[i];
            bFu[i] = F[i] - Fn + y[i] / (omega[i] * rho_scale);
            BSplineWeights<T, dim> spline(Xp, dx);
            grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                bFu[i] -= dt * g.v * dw.transpose() * Fn;
            });
            if (enable_visco) {
                int pc = particles.count;
                TM& FFn = (*FFn_pointer)[i];
                bFu[i + pc] = F[i + pc] - FFn + y[i + pc] / (omega[i + pc] * rho_scale);
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    bFu[i + pc] -= dt * g.v * dw.transpose() * FFn;
                });
            }
        });
        // P2G
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = TV::Zero();
        });
        Base::parallel_for_updating_grid([&](int i) {
            TV& Xp = Xarray[i];
            TM& Fn = (*Fn_pointer)[i];
            BSplineWeights<T, dim> spline(Xp, dx);
            grid.iterateKernel(spline, particle_base_offset[i], [&](const IV& node, T w, const TV& dw, GridState<T, dim>& g) {
                g.new_v += dt * rho_scale * omega[i] * omega[i] * bFu[i] * Fn.transpose() * dw;
            });
            if (enable_visco) {
                int pc = particles.count;
                TM& FFn = (*FFn_pointer)[i];
                grid.iterateKernel(spline, particle_base_offset[i], [&](const IV& node, T w, const TV& dw, GridState<T, dim>& g) {
                    g.new_v += dt * rho_scale * omega[i + pc] * omega[i + pc] * bFu[i + pc] * FFn.transpose() * dw;
                });
            }
        });
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            rhs.col(g.idx) += g.new_v;
        });

        cg.setTolerance(tolerance);
        cg.setRelativeTolerance(relative_tolerance);
        cg.setMaxIteration(global_max_iteration);
        total_cg_iterations += cg.solve(cg_objective, dv, rhs, false);
        // outputFile(out_a += step.max_dt / 100.0, total_cg_iterations);
    }

    template <class TCONST, class TPCONST>
    void initOmega()
    {
        ZIRAN_TIMER();
        auto ranges = particles.X.ranges;
        auto constitutive_model_name = AttributeName<TCONST>(TCONST::name());
        auto plastic_name = AttributeName<TPCONST>(TPCONST::name());
        if (!particles.exist(constitutive_model_name) || !particles.exist(plastic_name))
            return;
        tbb::parallel_for(ranges, [&](DisjointRanges& subrange) {
            DisjointRanges subset(subrange, particles.commonRanges(constitutive_model_name, element_measure_name<T>(), F_name<T, dim>(), plastic_name));
            for (auto iter = particles.subsetIter(subset, constitutive_model_name, element_measure_name<T>(), F_name<T, dim>(), plastic_name); iter; ++iter) {
                auto& constitutive_model = iter.template get<0>();
                auto& vol = iter.template get<1>();
                auto& plasticity_model = iter.template get<3>();
                int i = iter.entryId();
                TM Fn = F[i];
                TM U, V;
                TV sigma;
                singularValueDecomposition(Fn, U, sigma, V);
                TM A = constitutive_model.firstPiolaDerivativeDiagonal(sigma);
                makePD(A);
                if constexpr (!std::is_same<TPCONST, StvkWithHencky<T, dim>>::value) {
                    sigma = plasticity_model.projectSigma(constitutive_model, sigma);
                }
                T total = A.squaredNorm();
                T clamp_value = 1e-6;
                total += (makePD2D(constitutive_model.Bij(sigma, 0, 1, clamp_value))).squaredNorm();
                if constexpr (dim == 3) {
                    total += (makePD2D(constitutive_model.Bij(sigma, 0, 2, clamp_value))).squaredNorm();
                    total += (makePD2D(constitutive_model.Bij(sigma, 1, 2, clamp_value))).squaredNorm();
                }
                // omega[i] = std::sqrt(vol * ((T)2*constitutive_model.mu/3+constitutive_model.lambda));
                omega[i] = std::sqrt(vol * std::sqrt(total));
                if constexpr (std::is_same<TPCONST, StvkWithHencky<T, dim>>::value) {
                    int pc = particles.count;
                    singularValueDecomposition(F[i + pc], U, sigma, V);
                    //  TODO
                    A = plasticity_model.firstPiolaDerivativeDiagonal(sigma);
                    makePD(A);
                    total = A.squaredNorm();
                    clamp_value = 1e-6;
                    total += (makePD2D(plasticity_model.Bij(sigma, 0, 1, clamp_value))).squaredNorm();
                    if constexpr (dim == 3) {
                        total += (makePD2D(plasticity_model.Bij(sigma, 0, 2, clamp_value))).squaredNorm();
                        total += (makePD2D(plasticity_model.Bij(sigma, 1, 2, clamp_value))).squaredNorm();
                    }
                    // omega[i + pc] = std::sqrt(vol * ((T)2*plasticity_model.mu/3+plasticity_model.lambda));
                    omega[i + pc] = std::sqrt(vol * std::sqrt(total));
                }
            }
        });
    }

    void updateCollisionNodes()
    {
        Base::collision_nodes.clear();
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            TV vi = dv.col(g.idx) + vn.col(g.idx);
            const TV xi = node.template cast<T>() * dx;
            TM normal_basis;
            bool any_collision = AnalyticCollisionObject<T, dim>::
                multiObjectCollisionWithFakeSeparate(collision_objects, xi, vi, normal_basis);
            if (any_collision) {
                CollisionNode<T, dim> Z{ (int)g.idx, TM::Identity() - normal_basis * normal_basis.transpose() };
                mtx.lock();
                Base::collision_nodes.push_back(Z);
                mtx.unlock();
                dv.col(g.idx) = vi - vn.col(g.idx);
            }
        });
    }

    StdVector<TV> last_dv;

    void initStep()
    {
        ZIRAN_TIMER();

        bool flag = (dv.cols() == 0);

        dv.resize(dim, Base::num_nodes);
        vn.resize(dim, Base::num_nodes);
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            vn.col(g.idx) = g.v;
            dv.col(g.idx) = TV::Zero();
            // dv.col(g.idx) = dt * Base::gravity;
        });
        auto& Xarray = particles.X.array;
        last_dv.resize((int)particles.count, TV::Zero());
        std::vector<T> dv_w(Base::num_nodes, 0);
        Base::parallel_for_updating_grid([&](int i) {
            TV& Xp = Xarray[i];
            BSplineWeights<T, dim> spline(Xp, dx);
            grid.iterateKernel(spline, particle_base_offset[i], [&](const IV& node, T w, const TV& dw, GridState<T, dim>& g) {
                if (g.idx >= 0) {
                    dv.col(g.idx) += last_dv[i] * w;
                    dv_w[g.idx] += w;
                }
            });
        });
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            TV tmp = dv.col(g.idx) * dt / dv_w[g.idx];
            if ((tmp / dt).norm() < 0) {
                dv.col(g.idx) = TV::Zero();
            }
            else {
                dv.col(g.idx) = tmp;
            }
        });
        if (flag) {
            grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
                dv.col(g.idx) = Base::gravity * dt * 0;
            });
        }
        updateCollisionNodes();
        // init F
        F.resize((int)particles.count * 2);
        grid.iterateTouchedGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = TV::Zero();
        });
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = g.v + dv.col(g.idx);
        });
        if (particles.count > 0) {
            Fn_pointer = &(particles.DataManager::get(F_name<T, dim>()).array);
            if (enable_visco) {
                FFn_pointer = &(particles.DataManager::get(FEN_name<T, dim>()).array);
            }
        }
        tbb::parallel_for(0, (int)particles.count, [&](int i) {
            TV& Xp = Xarray[i];
            TM& Fn = (*Fn_pointer)[i];
            F[i] = Fn;
            BSplineWeights<T, dim> spline(Xp, dx);
            grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                F[i] += dt * g.new_v * dw.transpose() * Fn;
            });
            if (enable_visco) {
                int pc = particles.count;
                TM& FFn = (*FFn_pointer)[i];
                F[i + pc] = FFn;
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    F[i + pc] += dt * g.new_v * dw.transpose() * FFn;
                });
            }
        });
        // init omega
        StdVector<T> omega_old = omega;
        omega.resize((int)particles.count * 2, (T)1);
        if (use_elasticity_plasticity) {
            initOmega<StvkWithHencky<T, dim>, DummyPlasticity<T>>();
            initOmega<StvkWithHencky<T, dim>, DruckerPragerStvkHencky<T>>();
            initOmega<StvkWithHencky<T, dim>, VonMisesStvkHencky<T, dim>>();
        }
        else {
            initOmega<StvkWithHenckyIsotropic<T, dim>, StvkWithHencky<T, dim>>();
        }
        // init y
        StdVector<TM> y_old = y;
        int y_old_size = y.size();
        y.resize((int)particles.count * 2, TM::Zero());
        if (!enable_visco) {
            tbb::parallel_for(0, y_old_size / 2, [&](int i) {
                y[i] = y_old[i] * omega_old[i] / omega[i];
            });
        }
        else {
            int pc = particles.count;
            tbb::parallel_for(0, y_old_size / 2, [&](int i) {
                y[i] = y_old[i] * omega_old[i] / omega[i];
                y[i + pc] = y_old[i + y_old_size / 2] * omega_old[i + y_old_size / 2] / omega[i + pc];
            });
        }
        // init mass_matrix
        mass_matrix.resize((int)Base::num_nodes, 1);
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            mass_matrix(g.idx) = g.m;
        });
    }

    bool enable_visco = false;
    T viscosity_d;
    T viscosity_v;

    void initMultiplierAndPreconditioner()
    {
        ZIRAN_TIMER();
        if constexpr (build_matrix) {
            auto& Xarray = particles.X.array;
            // use new_v to store x
            // G2P
            int block_size = (dim == 2 ? 25 : 125);
            multiplier_value.resize((int)Base::num_nodes * block_size);
            multiplier_idx.resize((int)Base::num_nodes * block_size);
            std::fill(multiplier_value.begin(), multiplier_value.end(), 0);
            std::fill(multiplier_idx.begin(), multiplier_idx.end(), 0);
            Base::parallel_for_updating_grid([&](int i) {
                TV& Xp = Xarray[i];
                TM& Fn = (*Fn_pointer)[i];
                BSplineWeights<T, dim> spline(Xp, dx);
                std::vector<IV> cached_nodes;
                std::vector<int> cached_idx;
                std::vector<TV> cached_FTdw;
                grid.iterateKernel(spline, particle_base_offset[i], [&](const IV& node, T w, const TV& dw, GridState<T, dim>& g) {
                    if (g.idx >= 0) {
                        cached_nodes.push_back(node);
                        cached_idx.push_back((int)g.idx);
                        cached_FTdw.push_back(Fn.transpose() * dw);
                    }
                });
                T coeff = rho_scale * omega[i] * omega[i] * dt * dt;
                int cached_size = cached_nodes.size();
                for (int p = 0; p < cached_size; ++p) {
                    T* _value = &multiplier_value[cached_idx[p] * block_size];
                    int* _idx = &multiplier_idx[cached_idx[p] * block_size];
                    for (int q = 0; q < cached_size; ++q) {
                        int offset = 0;
                        if constexpr (dim == 2) {
                            offset = 5 * (cached_nodes[q](0) - cached_nodes[p](0) + 2) + cached_nodes[q](1) - cached_nodes[p](1) + 2;
                        }
                        else {
                            offset = 25 * (cached_nodes[q](0) - cached_nodes[p](0) + 2) + 5 * (cached_nodes[q](1) - cached_nodes[p](1) + 2) + cached_nodes[q](2) - cached_nodes[p](2) + 2;
                        }
                        _idx[offset] = cached_idx[q];
                        _value[offset] += coeff * cached_FTdw[p].dot(cached_FTdw[q]);
                    }
                }
                if (enable_visco) {
                    int pc = particles.count;
                    TM& FFn = (*FFn_pointer)[i];
                    std::vector<TV> cached_FFFF;
                    grid.iterateKernel(spline, particle_base_offset[i], [&](const IV& node, T w, const TV& dw, GridState<T, dim>& g) {
                        if (g.idx >= 0) {
                            cached_FFFF.push_back(FFn.transpose() * dw);
                        }
                    });
                    T ccccc = rho_scale * omega[i + pc] * omega[i + pc] * dt * dt;
                    for (int p = 0; p < cached_size; ++p) {
                        T* _value = &multiplier_value[cached_idx[p] * block_size];
                        for (int q = 0; q < cached_size; ++q) {
                            int offset = 0;
                            if constexpr (dim == 2) {
                                offset = 5 * (cached_nodes[q](0) - cached_nodes[p](0) + 2) + cached_nodes[q](1) - cached_nodes[p](1) + 2;
                            }
                            else {
                                offset = 25 * (cached_nodes[q](0) - cached_nodes[p](0) + 2) + 5 * (cached_nodes[q](1) - cached_nodes[p](1) + 2) + cached_nodes[q](2) - cached_nodes[p](2) + 2;
                            }
                            _value[offset] += ccccc * cached_FFFF[p].dot(cached_FFFF[q]);
                        }
                    }
                }
            });

            preconditioner.resize(dim, Base::num_nodes);
            grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
                g.new_v = g.m * TV::Ones();
            });
            Base::parallel_for_updating_grid([&](int i) {
                TV& Xp = Xarray[i];
                TM& Fn = (*Fn_pointer)[i];
                BSplineWeights<T, dim> spline(Xp, dx);
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    T coeff = rho_scale * omega[i] * omega[i] * dt * dt;
                    TM tmp = coeff * (TV::Ones() * dw.transpose() * Fn);
                    TV value = (tmp * Fn.transpose() * dw);
                    g.new_v += value;
                });
                if (enable_visco) {
                    int pc = particles.count;
                    TM& FFn = (*FFn_pointer)[i];
                    grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                        T ccccc = rho_scale * omega[i + pc] * omega[i + pc] * dt * dt;
                        TM ttt = ccccc * (TV::Ones() * dw.transpose() * FFn);
                        TV value = (ttt * FFn.transpose() * dw);
                        g.new_v += value;
                    });
                }
            });
            grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
                preconditioner.col(g.idx) = g.new_v;
            });
        }
    }

    template <class TCONST, class TPCONST>
    void FStepSolve(const T& localTol)
    {
        auto& Xarray = particles.X.array;
        auto constitutive_model_name = AttributeName<TCONST>(TCONST::name());
        auto plastic_name = AttributeName<TPCONST>(TPCONST::name());
        auto ranges = particles.X.ranges;
        if (!particles.exist(constitutive_model_name) || !particles.exist(plastic_name))
            return;
        tbb::parallel_for(ranges, [&](DisjointRanges& subrange) {
            DisjointRanges subset(subrange, particles.commonRanges(constitutive_model_name, plastic_name, element_measure_name<T>(), F_name<T, dim>()));
            for (auto iter = particles.subsetIter(subset, constitutive_model_name, plastic_name, element_measure_name<T>(), F_name<T, dim>()); iter; ++iter) {
                auto& c = iter.template get<0>();
                auto& p = iter.template get<1>();
                auto& vol = iter.template get<2>();
                int i = iter.entryId();
                TV& Xp = Xarray[i];
                TM& Fn = (*Fn_pointer)[i];
                // build rhs
                TM u_bar = -y[i] / (omega[i] * rho_scale);
                TM FDb = Fn + u_bar;
                BSplineWeights<T, dim> spline(Xp, dx);
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    FDb += dt * g.new_v * dw.transpose() * Fn;
                });
                // SVD rhs_raw
                TM U, V;
                TV sigma;
                singularValueDecomposition(FDb, U, sigma, V);
                TV zi = (U.transpose() * F[i] * V).diagonal();
                T coeff = rho_scale * omega[i] * omega[i];
                TV step;
                for (int j = 0; j < 5; j++) {
                    if constexpr (!std::is_same<TPCONST, StvkWithHencky<T, dim>>::value) {
                        TV zi_p;
                        TM zi_pd;
                        p.projectSigmaAndDerivative(c, zi, zi_p, zi_pd);
                        TV dE = c.firstPiolaDiagonal(zi_p);
                        TM ddE = c.firstPiolaDerivativeDiagonal(zi_p);
                        TV g = vol * dE + coeff * (zi - sigma);
                        TM P = vol * ddE * zi_pd + coeff * TM::Identity();
                        step = P.inverse() * (-g);
                        zi += step;
                        if (step.norm() < localTol)
                            break;
                    }
                    else {
                        TV dE = c.firstPiolaDiagonal(zi);
                        TM ddE = c.firstPiolaDerivativeDiagonal(zi);
                        TV g = vol * dE + coeff * (zi - sigma);
                        TM P = vol * ddE + coeff * TM::Identity();
                        step = P.inverse() * (-g);
                        zi += step;
                        if (step.norm() < localTol)
                            break;
                    }
                }
                // ZIRAN_ASSERT(step.norm() < localTol, "need larger rho_scale");
                F[i] = U * zi.asDiagonal() * V.transpose();
                if constexpr (std::is_same<TPCONST, StvkWithHencky<T, dim>>::value) {
                    int pc = particles.count;
                    TM& FFn = (*FFn_pointer)[i];
                    // build rhs
                    TM u_bar = -y[i + pc] / (omega[i + pc] * rho_scale);
                    TM FDb = FFn + u_bar;
                    BSplineWeights<T, dim> spline(Xp, dx);
                    grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                        FDb += dt * g.new_v * dw.transpose() * FFn;
                    });
                    // SVD rhs_raw
                    TM U, V;
                    TV sigma;
                    singularValueDecomposition(FDb, U, sigma, V);
                    TV zi = (U.transpose() * F[i + pc] * V).diagonal();
                    T coeff = rho_scale * omega[i + pc] * omega[i + pc];
                    TV step;
                    for (int j = 0; j < 20; j++) {
                        TV zi_p;
                        TM zi_pd;
                        {
                            T lambda = p.lambda;
                            T mu = p.mu;
                            T alpha = (T)2.0 * mu / viscosity_d;
                            T beta = (T)2.0 * ((T)2.0 * mu + lambda * dim) / ((T)9.0 * viscosity_v) - (T)2.0 * mu / (viscosity_d * dim);

                            TV epsilon_trial = zi.array().abs().log();
                            TV epsilon = (T)1.0 / ((T)1.0 + dt * alpha) * (epsilon_trial - dt * beta / ((T)1.0 + dt * (alpha + dim * beta)) * epsilon_trial.trace() * TV::Ones());
                            zi_p = epsilon.array().exp();

                            TM zi_p_m = zi_p.asDiagonal();
                            TV zi_inv = zi.array().inverse();
                            TM zi_inv_m = zi_inv.asDiagonal();
                            zi_pd = zi_p_m * ((T)1.0 / ((T)1.0 + dt * alpha)) * (zi_inv_m - dt * beta / ((T)1.0 + dt * (alpha + dim * beta)) * TV::Ones() * zi_inv.transpose());
                        }
                        TV dE = p.firstPiolaDiagonal(zi_p);
                        TM ddE = p.firstPiolaDerivativeDiagonal(zi_p);
                        TV g = vol * dE + coeff * (zi - sigma);
                        TM P = vol * ddE * zi_pd + coeff * TM::Identity();
                        step = P.inverse() * (-g);
                        zi += step;
                        if (step.norm() < localTol)
                            break;
                    }
                    // ZIRAN_ASSERT(step.norm() < localTol, "need larger rho_scale");
                    F[i + pc] = U * zi.asDiagonal() * V.transpose();
                }
            }
        });
    };

    // only work for non/ruiz
    void FStep(T localTol)
    {
        ZIRAN_TIMER();
        grid.iterateTouchedGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = TV::Zero();
        });
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = g.v + dv.col(g.idx);
        });
        local_max_iteration = 0;
        if (use_elasticity_plasticity) {
            FStepSolve<StvkWithHencky<T, dim>, DummyPlasticity<T>>(localTol);
            FStepSolve<StvkWithHencky<T, dim>, DruckerPragerStvkHencky<T>>(localTol);
            FStepSolve<StvkWithHencky<T, dim>, VonMisesStvkHencky<T, dim>>(localTol);
        }
        else {
            FStepSolve<StvkWithHenckyIsotropic<T, dim>, StvkWithHencky<T, dim>>(localTol);
        }
    }

    void uStep()
    {
        ZIRAN_TIMER();
        auto& Xarray = particles.X.array;

        // use new_v to store dv
        // G2P
        grid.iterateTouchedGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = TV::Zero();
        });
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = g.v + dv.col(g.idx);
        });
        tbb::parallel_for(0, (int)particles.count, [&](int i) {
            TV& Xp = Xarray[i];
            TM& Fn = (*Fn_pointer)[i];

            TM tmp = F[i] - Fn;
            BSplineWeights<T, dim> spline(Xp, dx);
            grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                tmp -= dt * g.new_v * dw.transpose() * Fn;
            });
            y[i] += rho_scale * omega[i] * tmp;
            if (enable_visco) {
                int pc = particles.count;
                TM& FFn = (*FFn_pointer)[i];
                TM ttt = F[i + pc] - FFn;
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    ttt -= dt * g.new_v * dw.transpose() * FFn;
                });
                y[i + pc] += rho_scale * omega[i + pc] * ttt;
            }
        });
    }

    T admmResidual()
    {
        Base::force->updatePositionBasedState();
        Base::inertia->updatePositionBasedState();
        TVStack residual;
        residual.resize(dim, Base::num_nodes);
        tbb::parallel_for(tbb::blocked_range<int>(0, residual.cols(), 256),
            [&](const tbb::blocked_range<int>& range) {
                int start = range.begin();
                int length = (range.end() - range.begin());
                TV dtg = dt * Base::gravity;
                residual.middleCols(start, length) = dtg * mass_matrix.segment(start, length).transpose();
            });
        Base::addScaledForces(dt, residual);
        Base::inertia->addScaledForces(dt, residual);
        for (auto iter = Base::collision_nodes.begin(); iter != Base::collision_nodes.end(); ++iter) {
            int node_id = (*iter).node_id;
            (*iter).project(residual.col(node_id));
        }
        ZIRAN_ASSERT(residual.cols() == Base::num_nodes);
        T norm_sq = tbb::parallel_reduce(
            tbb::blocked_range<int>(0, residual.cols(), 256), (T)0,
            [&](const tbb::blocked_range<int>& range, T ns) -> T {
                int start = range.begin();
                int length = (range.end() - range.begin());
                const auto& r_block = residual.middleCols(start, length);
                const auto& mass_block = mass_matrix.segment(start, length).transpose();
                return ns + ((r_block.colwise().squaredNorm()).array() / mass_block.array()).sum();
            },
            [](T a, T b) -> T { return a + b; });
        Base::force->restoreStrain();

        T residual_current = std::sqrt(norm_sq);
        outputResidual(out_a += step.max_dt / 200.0, residual_current);
        ZIRAN_INFO("admm residual ", residual_current, ", total cg iterations = ", total_cg_iterations, ", rho_scale = ", rho_scale);

        return residual_current;
    }

    TVStack old_dv;
    void updateRhoScaleAbsolute()
    {
        auto& Xarray = particles.X.array;
        // use new_v to store dv
        // G2P
        T prime_residual = 0;
        grid.iterateTouchedGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = TV::Zero();
        });
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v = dv.col(g.idx);
        });
        tbb::parallel_for(0, (int)particle_group.size(), [&](int group_idx) {
            for (int idx = particle_group[group_idx].first; idx <= particle_group[group_idx].second; ++idx) {
                int i = particle_order[idx];
                TV& Xp = Xarray[i];
                TM& Fn = (*Fn_pointer)[i];
                BSplineWeights<T, dim> spline(Xp, dx);
                TM tmp = Fn - F[i];
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    tmp += dt * g.v * dw.transpose() * Fn;
                    tmp += dt * g.new_v * dw.transpose() * Fn;
                });
                mtx.lock();
                prime_residual += (tmp * omega[i]).squaredNorm();
                mtx.unlock();
            }
        });
        prime_residual = std::sqrt(prime_residual);

        T dual_residual = 0;
        grid.iterateGrid([&](IV node, GridState<T, dim>& g) {
            g.new_v -= old_dv.col(g.idx);
        });
        tbb::parallel_for(0, (int)particle_group.size(), [&](int group_idx) {
            for (int idx = particle_group[group_idx].first; idx <= particle_group[group_idx].second; ++idx) {
                int i = particle_order[idx];
                TV& Xp = Xarray[i];
                TM& Fn = (*Fn_pointer)[i];
                BSplineWeights<T, dim> spline(Xp, dx);
                TM tmp = TM::Zero();
                grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                    tmp += dt * g.new_v * dw.transpose() * Fn;
                });
                mtx.lock();
                dual_residual += (rho_scale * tmp * omega[i] * omega[i]).squaredNorm();
                mtx.unlock();
            }
        });
        dual_residual = std::sqrt(dual_residual);
        ZIRAN_INFO("prime residual : ", prime_residual, "\t\tdual residual : ", dual_residual, "\t\trho_scale :", rho_scale);
    }

    void admmStep()
    {
        ZIRAN_ASSERT(!use_ruiz, "please use AdmmSimulationFull.h");
        ZIRAN_ASSERT(over_relaxation_alpha == (T)1, "please use AdmmSimulationFull.h");

        Base::force->backupStrain();
        initStep();
        initMultiplierAndPreconditioner();

        out_a = step.time;
        // outputFile(out_a, total_cg_iterations);
        for (int tim = 0; tim < admm_max_iterations; ++tim) {
            updateCollisionNodes();
            // T residual_current = admmResidual();
            T residual_current = 1;
            FStep(local_tolerance);
            dvStep(global_tolerance, std::min((T)0.5, (T)1 * std::sqrt(std::max(residual_current, (T)1e-6))));
            uStep();
        }
        // same with BE method
        Base::constructNewVelocityFromNewtonResult();
        auto& Xarray = particles.X.array;
        tbb::parallel_for(0, (int)particles.count, [&](int i) {
            TV& Xp = Xarray[i];
            BSplineWeights<T, dim> spline(Xp, dx);
            T last_dv_w = 0;
            last_dv[i] = TV::Zero();
            grid.iterateKernel(spline, particle_base_offset[i], [&](IV node, T w, TV dw, GridState<T, dim>& g) {
                if (g.idx >= 0) {
                    last_dv[i] += dv.col(g.idx) * w;
                    last_dv_w += w;
                }
            });
            last_dv[i] /= (last_dv_w * dt);
        });
    }

    virtual void advanceOneTimeStep(double dt)
    {
        ZIRAN_TIMER();
        ZIRAN_INFO("Advance one time step from time ", std::setw(7), step.time, " with                     dt = ", dt);

        Base::reinitialize();

        ZIRAN_ASSERT(!Base::symplectic, "It should be implicit P2G transfer.");

        Base::particlesToGrid();
        admmStep();
        for (size_t k = 0; k < collision_objects.size(); ++k)
            if (collision_objects[k]->updateState)
                collision_objects[k]->updateState(step.time + dt, *collision_objects[k]);
        Base::gridToParticles(dt);
        /*
        tbb::parallel_for(0, (int)particles.count, [&](int i) {
            TM& Fn = (*Fn_pointer)[i];
            TM U, V;
            TV sigma;
            singularValueDecomposition(Fn, U, sigma, V);
            MATH_TOOLS::clamp(sigma, 0.2, 5.0);
            Fn = U * sigma.asDiagonal() * V.transpose();
        });

        tbb::parallel_for(0, (int)particles.count, [&](int i) {
            TM& FFn = (*FFn_pointer)[i];
            TM U, V;
            TV sigma;
            singularValueDecomposition(FFn, U, sigma, V);
            MATH_TOOLS::clamp(sigma, 0.2, 5.0);
            FFn = U * sigma.asDiagonal() * V.transpose();
        });
*/
    }

    const char* name() override { return "admm"; }
};
} // namespace ZIRAN

#endif

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API