diff options
Diffstat (limited to 'src/lib/netlist/solver/nld_matrix_solver.h')
-rw-r--r-- | src/lib/netlist/solver/nld_matrix_solver.h | 659 |
1 files changed, 374 insertions, 285 deletions
diff --git a/src/lib/netlist/solver/nld_matrix_solver.h b/src/lib/netlist/solver/nld_matrix_solver.h index 29017add8a9..c0b792fd597 100644 --- a/src/lib/netlist/solver/nld_matrix_solver.h +++ b/src/lib/netlist/solver/nld_matrix_solver.h @@ -1,375 +1,464 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * nld_matrix_solver.h - * - */ #ifndef NLD_MATRIX_SOLVER_H_ #define NLD_MATRIX_SOLVER_H_ -#include "netlist/nl_base.h" -#include "netlist/nl_errstr.h" +// Names +// spell-checker: words Raphson, Seidel + +/// +/// \file nld_matrix_solver.h +/// + +#include "nl_errstr.h" +#include "nltypes.h" + +#include "../core/analog.h" +#include "../core/device.h" +#include "../core/device_macros.h" +#include "../core/param.h" + #include "plib/palloc.h" +#include "plib/penum.h" #include "plib/pmatrix2d.h" +#include "plib/pmatrix_cr.h" +#include "plib/pmempool.h" #include "plib/putil.h" #include "plib/vector_ops.h" -#include <cmath> +#include <numeric> -namespace netlist -{ -namespace devices +// FIXME: remove again + +#define PFDEBUG(x) + +namespace netlist::solver { - /* FIXME: these should become proper devices */ - struct solver_parameters_t + enum static_compile_target { - bool m_pivot; - nl_double m_accuracy; - nl_double m_dynamic_lte; - nl_double m_min_timestep; - nl_double m_max_timestep; - nl_double m_gs_sor; - bool m_dynamic_ts; - std::size_t m_gs_loops; - std::size_t m_nr_loops; - netlist_time m_nr_recalc_delay; - bool m_use_gabs; - bool m_use_linear_prediction; + CXX_EXTERNAL_C, + CXX_STATIC }; - - class terms_for_net_t : plib::nocopyassignmove + // clang-format off + + PENUM(matrix_sort_type_e, + NOSORT, + ASCENDING, + DESCENDING, + PREFER_IDENTITY_TOP_LEFT, + PREFER_BAND_MATRIX + ) + + PENUM(matrix_type_e, + SOR_MAT, + MAT_CR, + MAT, + SM, + W, + SOR, + GMRES + ) + + PENUM(matrix_fp_type_e, + FLOAT + , DOUBLE + , LONGDOUBLE + , FLOATQ128 + ) + + // clang-format on + + using arena_type = plib::mempool_arena<plib::aligned_arena<>, 1024>; + using static_compile_container = std::vector<std::pair<pstring, pstring>>; + + struct solver_parameter_defaults { - public: - terms_for_net_t(); - - void clear(); - - void add(terminal_t *term, int net_other, bool sorted); - - std::size_t count() const { return m_terms.size(); } - - terminal_t **terms() { return m_terms.data(); } + static constexpr nl_fptype m_freq() { return nlconst::magic(48000.0); } - std::size_t m_railstart; + // iteration parameters + static constexpr nl_fptype m_gs_sor() { return nlconst::magic(1.059); } + static constexpr matrix_type_e m_method() + { + return matrix_type_e::MAT_CR; + } + static constexpr matrix_fp_type_e m_fp_type() + { + return matrix_fp_type_e::DOUBLE; + } + static constexpr nl_fptype m_reltol() { return nlconst::magic(1e-3); } + static constexpr nl_fptype m_vntol() { return nlconst::magic(1e-7); } + static constexpr nl_fptype m_accuracy() { return nlconst::magic(1e-7); } + static constexpr std::size_t m_nr_loops() { return 250; } + static constexpr std::size_t m_gs_loops() { return 50; } + + // general parameters + static constexpr nl_fptype m_gmin() { return nlconst::magic(1e-9); } + static constexpr bool m_pivot() { return false; } + static constexpr nl_fptype m_nr_recalc_delay() + { + return netlist_time::quantum().as_fp<nl_fptype>(); + } + static constexpr int m_parallel() { return 0; } - std::vector<unsigned> m_nz; /* all non zero for multiplication */ - std::vector<unsigned> m_nzrd; /* non zero right of the diagonal for elimination, may include RHS element */ - std::vector<unsigned> m_nzbd; /* non zero below of the diagonal for elimination */ + static constexpr nl_fptype m_min_ts_ts() + { + return nlconst::magic(1e-9); + } + // automatic time step + static constexpr bool m_dynamic_ts() { return false; } + static constexpr nl_fptype m_dynamic_lte() + { + return nlconst::magic(1e-5); + } + static constexpr nl_fptype m_dynamic_min_ts() + { + return nlconst::magic(1e-6); + } - /* state */ - nl_double m_last_V; - nl_double m_DD_n_m_1; - nl_double m_h_n_m_1; + // matrix sorting + static constexpr matrix_sort_type_e m_sort_type() + { + return matrix_sort_type_e::PREFER_IDENTITY_TOP_LEFT; + } - std::vector<int> m_connected_net_idx; - private: - std::vector<terminal_t *> m_terms; + // special + static constexpr bool m_use_gabs() { return true; } + static solver_parameter_defaults &get_instance() + { + static solver_parameter_defaults s; + return s; + } }; - class proxied_analog_output_t : public analog_output_t + struct solver_parameters_t { - public: + template <typename D> + solver_parameters_t(device_t &parent, const pstring &prefix, + D &defaults) + : m_freq(parent, prefix + "FREQ", defaults.m_freq()) + + // iteration parameters + , m_gs_sor(parent, prefix + "SOR_FACTOR", defaults.m_gs_sor()) + , m_method(parent, prefix + "METHOD", defaults.m_method()) + , m_fp_type(parent, prefix + "FPTYPE", defaults.m_fp_type()) + , m_reltol(parent, prefix + "RELTOL", + defaults.m_reltol()) //!< SPICE RELTOL parameter + , m_vntol(parent, prefix + "VNTOL", defaults.m_vntol()) //!< SPICE VNTOL + //!< parameter + , m_accuracy(parent, prefix + "ACCURACY", + defaults.m_accuracy()) //!< Iterative solver accuracy + , m_nr_loops(parent, prefix + "NR_LOOPS", + defaults.m_nr_loops()) //!< Maximum number of + //!< Newton-Raphson loops + , m_gs_loops(parent, prefix + "GS_LOOPS", + defaults.m_gs_loops()) //!< Maximum number of Gauss-Seidel + //!< loops + + // general parameters + , m_gmin(parent, prefix + "GMIN", defaults.m_gmin()) + , m_pivot(parent, prefix + "PIVOT", defaults.m_pivot()) //!< use + //!< pivoting on + //!< supported + //!< solvers + , m_nr_recalc_delay(parent, prefix + "NR_RECALC_DELAY", + defaults.m_nr_recalc_delay()) //!< Delay to next + //!< solve attempt if + //!< nr loops exceeded + , m_parallel(parent, prefix + "PARALLEL", defaults.m_parallel()) + , m_min_ts_ts(parent, prefix + "MIN_TS_TS", + defaults.m_min_ts_ts()) //!< The minimum time step for + //!< solvers with time stepping + //!< devices. + + // automatic time step + , m_dynamic_ts(parent, prefix + "DYNAMIC_TS", + defaults.m_dynamic_ts()) //!< Use dynamic time stepping + , m_dynamic_lte(parent, prefix + "DYNAMIC_LTE", + defaults.m_dynamic_lte()) //!< dynamic time stepping + //!< slope + , m_dynamic_min_ts(parent, prefix + "DYNAMIC_MIN_TIMESTEP", + defaults.m_dynamic_min_ts()) //!< smallest time step + //!< allowed + + // matrix sorting + , m_sort_type(parent, prefix + "SORT_TYPE", defaults.m_sort_type()) + + // special + , m_use_gabs(parent, prefix + "USE_GABS", defaults.m_use_gabs()) + , m_min_time_step(m_dynamic_min_ts()) + { + m_max_time_step = netlist_time::from_fp(plib::reciprocal(m_freq())) + .as_fp<decltype(m_max_time_step)>(); - proxied_analog_output_t(core_device_t &dev, const pstring &aname, analog_net_t *pnet) - : analog_output_t(dev, aname) - , m_proxied_net(pnet) - { } + if (m_dynamic_ts) + { + m_max_time_step *= 1; // NL_FCONST(1000.0); + } + else + { + m_min_time_step = m_max_time_step; + } + } - analog_net_t *proxied_net() const { return m_proxied_net;} - private: - analog_net_t *m_proxied_net; // only for proxy nets in analog input logic + param_fp_t m_freq; + param_fp_t m_gs_sor; + param_enum_t<matrix_type_e> m_method; + param_enum_t<matrix_fp_type_e> m_fp_type; + param_fp_t m_reltol; + param_fp_t m_vntol; + param_fp_t m_accuracy; + param_num_t<std::size_t> m_nr_loops; + param_num_t<std::size_t> m_gs_loops; + param_fp_t m_gmin; + param_logic_t m_pivot; + param_fp_t m_nr_recalc_delay; + param_int_t m_parallel; + param_fp_t m_min_ts_ts; + param_logic_t m_dynamic_ts; + param_fp_t m_dynamic_lte; + param_fp_t m_dynamic_min_ts; + param_enum_t<matrix_sort_type_e> m_sort_type; + + param_logic_t m_use_gabs; + + nl_fptype m_min_time_step; + nl_fptype m_max_time_step; }; - class matrix_solver_t : public device_t + class terms_for_net_t { public: - using list_t = std::vector<matrix_solver_t *>; + terms_for_net_t(arena_type &arena, analog_net_t *net = nullptr); - enum eSortType - { - NOSORT, - ASCENDING, - DESCENDING, - PREFER_IDENTITY_TOP_LEFT, - PREFER_BAND_MATRIX - }; - - void setup(analog_net_t::list_t &nets) - { - vsetup(nets); - } - - void solve_base(); + void clear(); - /* after every call to solve, update inputs must be called. - * this can be done as well as a batch to ease parallel processing. - */ - const netlist_time solve(netlist_time now); - void update_inputs(); + void add_terminal(terminal_t *term, int net_other, bool sorted); - bool has_dynamic_devices() const { return m_dynamic_devices.size() > 0; } - bool has_timestep_devices() const { return m_step_devices.size() > 0; } + std::size_t count() const noexcept { return m_terms.size(); } - void update_forced(); - void update_after(const netlist_time after) - { - m_Q_sync.net().toggle_and_push_to_queue(after); - } + std::size_t rail_start() const noexcept { return m_rail_start; } - /* netdevice functions */ - NETLIB_UPDATEI(); - NETLIB_RESETI(); + terminal_t **terms() noexcept { return m_terms.data(); } - public: - int get_net_idx(detail::net_t *net); - std::pair<int, int> get_left_right_of_diag(std::size_t row, std::size_t diag); - double get_weight_around_diag(std::size_t row, std::size_t diag); + nl_fptype getV() const noexcept { return m_net->Q_Analog(); } - virtual void log_stats(); + void setV(nl_fptype v) noexcept { m_net->set_Q_Analog(v); } - virtual std::pair<pstring, pstring> create_solver_code() + bool is_net(const analog_net_t *net) const noexcept { - return std::pair<pstring, pstring>("", plib::pfmt("/* solver doesn't support static compile */\n\n")); + return net == m_net; } - /* return number of floating point operations for solve */ - std::size_t ops() { return m_ops; } + void set_rail_start(std::size_t val) noexcept { m_rail_start = val; } - protected: + PALIGNAS_VECTOROPT() + + plib::arena_vector<arena_type, unsigned> m_nz; //!< all non zero for + //!< multiplication + plib::arena_vector<arena_type, unsigned> m_nzrd; //!< non zero right of + //!< the diagonal for + //!< elimination, may + //!< include RHS + //!< element + plib::arena_vector<arena_type, unsigned> m_nzbd; //!< non zero below of + //!< the diagonal for + //!< elimination - matrix_solver_t(netlist_state_t &anetlist, const pstring &name, - eSortType sort, const solver_parameters_t *params); + plib::arena_vector<arena_type, int> m_connected_net_idx; - void sort_terms(eSortType sort); + private: + plib::arena_vector<arena_type, terminal_t *> m_terms; + analog_net_t *m_net; + std::size_t m_rail_start; + }; - void setup_base(analog_net_t::list_t &nets); - void update_dynamic(); + class proxied_analog_output_t : public analog_output_t + { + public: + proxied_analog_output_t(core_device_t &dev, const pstring &aname, + analog_net_t *pnet) + : analog_output_t(dev, aname) + , m_proxied_net(pnet) + { + } - virtual void vsetup(analog_net_t::list_t &nets) = 0; - virtual unsigned vsolve_non_dynamic(const bool newton_raphson) = 0; + analog_net_t *proxied_net() const { return m_proxied_net; } - netlist_time compute_next_timestep(const double cur_ts); - /* virtual */ void add_term(std::size_t net_idx, terminal_t *term); + private: + analog_net_t *m_proxied_net; // only for proxy nets in analog input + // logic + }; - template <typename T> - void store(const T & V); + class matrix_solver_t : public device_t + { + public: + using list_t = std::vector<matrix_solver_t *>; + using fptype = nl_fptype; + using net_list_t = std::vector<analog_net_t *>; - template <typename T> - auto delta(const T & V) -> typename std::decay<decltype(V[0])>::type; + // after every call to solve, update inputs must be called. + // this can be done as well as a batch to ease parallel processing. - template <typename T> - void build_LE_A(T &child); - template <typename T> - void build_LE_RHS(T &child); + netlist_time solve(netlist_time_ext now, const char *source); + void update_inputs(); - void set_pointers() + std::size_t dynamic_device_count() const noexcept { - const std::size_t iN = this->m_nets.size(); + return m_dynamic_funcs.size(); + } + std::size_t time_step_device_count() const noexcept + { + return m_step_funcs.size(); + } - std::size_t max_count = 0; - std::size_t max_rail = 0; - for (std::size_t k = 0; k < iN; k++) - { - max_count = std::max(max_count, m_terms[k]->count()); - max_rail = std::max(max_rail, m_terms[k]->m_railstart); - } + /// \brief reschedule solver execution + /// + /// Calls reschedule on main solver + /// + void reschedule(netlist_time ts); + + /// \brief Immediately solve system at current time + /// + /// This should only be called from update and update_param events. + /// It's purpose is to bring voltage values to the current time step. + /// This will be called BEFORE updating object properties. + void solve_now() + { + // this should only occur outside of execution and thus + // using time should be safe. - m_mat_ptr.resize(iN, max_rail+1); - m_gtn.resize(iN, max_count); - m_gonn.resize(iN, max_count); - m_Idrn.resize(iN, max_count); - m_connected_net_Vn.resize(iN, max_count); + [[maybe_unused]] const netlist_time new_time_step = solve( + exec().time(), "solve_now"); - for (std::size_t k = 0; k < iN; k++) - { - auto count = m_terms[k]->count(); + update_inputs(); - for (std::size_t i = 0; i < count; i++) - { - m_terms[k]->terms()[i]->set_ptrs(&m_gtn[k][i], &m_gonn[k][i], &m_Idrn[k][i]); - m_connected_net_Vn[k][i] = m_terms[k]->terms()[i]->connected_terminal()->net().Q_Analog_state_ptr(); - } + if (time_step_device_count() > 0) + { + this->reschedule(netlist_time::from_fp( + m_params.m_dynamic_ts ? m_params.m_min_time_step + : m_params.m_max_time_step)); } } - template <typename AP, typename FT> - void fill_matrix(std::size_t N, AP &tcr, FT &RHS) + template <typename F> + void change_state(F f) { - for (std::size_t k = 0; k < N; k++) + // We only need to update the net first if this is a time stepping + // net + if (time_step_device_count() > 0) { - auto *net = m_terms[k].get(); - auto **tcr_r = &(tcr[k][0]); - - const std::size_t term_count = net->count(); - const std::size_t railstart = net->m_railstart; - const auto &go = m_gonn[k]; - const auto > = m_gtn[k]; - const auto &Idr = m_Idrn[k]; - const auto &cnV = m_connected_net_Vn[k]; - - for (std::size_t i = 0; i < railstart; i++) - *tcr_r[i] += go[i]; - - typename FT::value_type gtot_t = 0.0; - typename FT::value_type RHS_t = 0.0; - - for (std::size_t i = 0; i < term_count; i++) - { - gtot_t += gt[i]; - RHS_t += Idr[i]; - } - // FIXME: Code above is faster than vec_sum - Check this - #if 0 - auto gtot_t = plib::vec_sum<FT>(term_count, m_gt); - auto RHS_t = plib::vec_sum<FT>(term_count, m_Idr); - #endif - - for (std::size_t i = railstart; i < term_count; i++) - { - RHS_t += (/*m_Idr[i]*/ (- go[i]) * *cnV[i]); - } - - RHS[k] = RHS_t; - // update diagonal element ... - *tcr_r[railstart] += gtot_t; //mat.A[mat.diag[k]] += gtot_t; + [[maybe_unused]] const netlist_time new_time_step = solve( + exec().time(), "change_state"); + update_inputs(); } - + f(); + if (time_step_device_count() > 0) + { + PFDEBUG(printf("here2\n");) + this->reschedule(netlist_time::from_fp(m_params.m_min_ts_ts())); + } + else + this->reschedule(netlist_time::quantum()); } - template <typename T> - using aligned_alloc = plib::aligned_allocator<T, PALIGN_VECTOROPT>; + NETLIB_RESETI(); - plib::pmatrix2d<nl_double, aligned_alloc<nl_double>> m_gonn; - plib::pmatrix2d<nl_double, aligned_alloc<nl_double>> m_gtn; - plib::pmatrix2d<nl_double, aligned_alloc<nl_double>> m_Idrn; - plib::pmatrix2d<nl_double *, aligned_alloc<nl_double *>> m_mat_ptr; - plib::pmatrix2d<nl_double *, aligned_alloc<nl_double *>> m_connected_net_Vn; + virtual void log_stats(); - plib::pmatrix2d<nl_double> m_test; + virtual std::pair<pstring, pstring> create_solver_code( + [[maybe_unused]] solver::static_compile_target target) + { + return {"", + plib::pfmt("// solver doesn't support static compile\n\n")}; + } - std::vector<plib::unique_ptr<terms_for_net_t>> m_terms; - std::vector<analog_net_t *> m_nets; - std::vector<pool_owned_ptr<proxied_analog_output_t>> m_inps; + // return number of floating point operations for solve + constexpr std::size_t ops() const { return m_ops; } - std::vector<plib::unique_ptr<terms_for_net_t>> m_rails_temp; + protected: + matrix_solver_t(devices::nld_solver &main_solver, const pstring &name, + const net_list_t &nets, + const solver_parameters_t *params); + + virtual void upstream_solve_non_dynamic() = 0; + virtual netlist_time + compute_next_time_step(fptype cur_ts, fptype min_ts, fptype max_ts) + = 0; + virtual bool check_err() const = 0; + virtual void store() = 0; + virtual void backup() = 0; + virtual void restore() = 0; + + std::size_t max_rail_start() const noexcept + { + std::size_t max_rail = 0; + for (const auto &term : m_terms) + max_rail = std::max(max_rail, term.rail_start()); + return max_rail; + } const solver_parameters_t &m_params; + arena_type m_arena; - state_var<int> m_stat_calculations; - state_var<int> m_stat_newton_raphson; - state_var<int> m_stat_vsolver_calls; - state_var<int> m_iterative_fail; - state_var<int> m_iterative_total; - - private: + plib::pmatrix2d_vrl<arena_type, fptype> m_gonn; + plib::pmatrix2d_vrl<arena_type, fptype> m_gtn; + plib::pmatrix2d_vrl<arena_type, fptype> m_Idrn; + plib::pmatrix2d_vrl<arena_type, fptype *> m_connected_net_Vn; - state_var<netlist_time> m_last_step; - std::vector<core_device_t *> m_step_devices; - std::vector<core_device_t *> m_dynamic_devices; + state_var<std::size_t> m_iterative_fail; + state_var<std::size_t> m_iterative_total; - logic_input_t m_fb_sync; - logic_output_t m_Q_sync; + std::vector<terms_for_net_t> m_terms; // setup only - /* calculate matrix */ - void setup_matrix(); - - void step(const netlist_time &delta); - - std::size_t m_ops; - const eSortType m_sort; - }; - - template <typename T> - auto matrix_solver_t::delta(const T & V) -> typename std::decay<decltype(V[0])>::type - { - /* NOTE: Ideally we should also include currents (RHS) here. This would - * need a reevaluation of the right hand side after voltages have been updated - * and thus belong into a different calculation. This applies to all solvers. - */ - - const std::size_t iN = this->m_terms.size(); - typename std::decay<decltype(V[0])>::type cerr = 0; - for (std::size_t i = 0; i < iN; i++) - cerr = std::max(cerr, std::abs(V[i] - this->m_nets[i]->Q_Analog())); - return cerr; - } - - template <typename T> - void matrix_solver_t::store(const T & V) - { - const std::size_t iN = this->m_terms.size(); - for (std::size_t i = 0; i < iN; i++) - this->m_nets[i]->set_Q_Analog(V[i]); - } + private: + // base setup - called from constructor + void setup_base(setup_t &setup, const net_list_t &nets) noexcept(false); - template <typename T> - void matrix_solver_t::build_LE_A(T &child) - { - using float_type = typename T::float_type; - static_assert(std::is_base_of<matrix_solver_t, T>::value, "T must derive from matrix_solver_t"); + bool solve_nr_base(); + netlist_time newton_loops_exceeded(netlist_time delta); - const std::size_t iN = child.size(); - for (std::size_t k = 0; k < iN; k++) - { - terms_for_net_t *terms = m_terms[k].get(); - float_type * Ak = &child.A(k, 0ul); + void sort_terms(matrix_sort_type_e sort); - for (std::size_t i=0; i < iN; i++) - Ak[i] = 0.0; + void update_dynamic() noexcept; + void step(detail::time_step_type ts_type, netlist_time delta) noexcept; - const std::size_t terms_count = terms->count(); - const std::size_t railstart = terms->m_railstart; - const float_type * const gt = m_gtn[k]; + int get_net_idx(const analog_net_t *net) const noexcept; + std::pair<int, int> + get_left_right_of_diagonal(std::size_t irow, std::size_t idiag); + fptype get_weight_around_diagonal(std::size_t row, std::size_t diag); - { - float_type akk = 0.0; - for (std::size_t i = 0; i < terms_count; i++) - akk += gt[i]; - - Ak[k] = akk; - } + void add_term(std::size_t net_idx, terminal_t *term) noexcept(false); - const float_type * const go = m_gonn[k]; - int * net_other = terms->m_connected_net_idx.data(); + // calculate matrix + void setup_matrix(); - for (std::size_t i = 0; i < railstart; i++) - Ak[net_other[i]] += go[i]; - } - } + void set_pointers(); - template <typename T> - void matrix_solver_t::build_LE_RHS(T &child) - { - static_assert(std::is_base_of<matrix_solver_t, T>::value, "T must derive from matrix_solver_t"); - using float_type = typename T::float_type; + analog_net_t *get_connected_net(terminal_t *term); - const std::size_t iN = child.size(); - for (std::size_t k = 0; k < iN; k++) - { - float_type rhsk_a = 0.0; - float_type rhsk_b = 0.0; + devices::nld_solver &m_main_solver; - const std::size_t terms_count = m_terms[k]->count(); - const float_type * const go = m_gonn[k]; - const float_type * const Idr = m_Idrn[k]; - const float_type * const * other_cur_analog = m_connected_net_Vn[k]; + state_var<std::size_t> m_stat_calculations; + state_var<std::size_t> m_stat_newton_raphson; + state_var<std::size_t> m_stat_newton_raphson_fail; + state_var<std::size_t> m_stat_vsolver_calls; - for (std::size_t i = 0; i < terms_count; i++) - rhsk_a = rhsk_a + Idr[i]; + state_var<netlist_time_ext> m_last_step; + plib::arena_vector<arena_type, nl_delegate_ts> m_step_funcs; + plib::arena_vector<arena_type, nl_delegate_dyn> m_dynamic_funcs; + plib::arena_vector<arena_type, + device_arena::unique_ptr<proxied_analog_output_t>> + m_inputs; - for (std::size_t i = m_terms[k]->m_railstart; i < terms_count; i++) - //rhsk = rhsk + go[i] * terms[i]->m_otherterm->net().as_analog().Q_Analog(); - rhsk_b = rhsk_b - go[i] * *other_cur_analog[i]; + std::size_t m_ops; - child.RHS(k) = rhsk_a + rhsk_b; - } - } + std::vector<terms_for_net_t> m_rails_temp; // setup only + }; -} //namespace devices -} // namespace netlist +} // namespace netlist::solver -#endif /* NLD_MS_DIRECT_H_ */ +#endif // NLD_MS_DIRECT_H_ |