diff --git a/src/processors/CircuitSimulatorInterface.cpp b/src/processors/CircuitSimulatorInterface.cpp index 0c5ced3..6f2f221 100644 --- a/src/processors/CircuitSimulatorInterface.cpp +++ b/src/processors/CircuitSimulatorInterface.cpp @@ -735,8 +735,11 @@ std::shared_ptr CircuitSimulatorExporterModel::fa else if (programName == CircuitSimulatorExporterModels::LTSPICE) { return std::make_shared(); } + else if (programName == CircuitSimulatorExporterModels::PLECS) { + return std::make_shared(); + } else - throw ModelNotAvailableException("Unknown Circuit Simulator program, available options are: {SIMBA, NGSPICE, LTSPICE}"); + throw ModelNotAvailableException("Unknown Circuit Simulator program, available options are: {SIMBA, NGSPICE, LTSPICE, PLECS}"); } std::string CircuitSimulatorExporter::export_magnetic_as_subcircuit(Magnetic magnetic, double frequency, double temperature, std::optional outputFilename, std::optional filePathOrFile, CircuitSimulatorExporterCurveFittingModes mode) { diff --git a/src/processors/CircuitSimulatorInterface.h b/src/processors/CircuitSimulatorInterface.h index 1818c46..280bc87 100644 --- a/src/processors/CircuitSimulatorInterface.h +++ b/src/processors/CircuitSimulatorInterface.h @@ -408,7 +408,8 @@ enum class CircuitSimulatorExporterModels : int { SIMBA, NGSPICE, LTSPICE, - NL5 + NL5, + PLECS }; void from_json(const json & j, CircuitSimulatorExporterModels & x); @@ -419,6 +420,7 @@ inline void from_json(const json & j, CircuitSimulatorExporterModels & x) { else if (j == "NgSpice") x = CircuitSimulatorExporterModels::NGSPICE; else if (j == "LtSpice") x = CircuitSimulatorExporterModels::LTSPICE; else if (j == "NL5") x = CircuitSimulatorExporterModels::NL5; + else if (j == "PLECS") x = CircuitSimulatorExporterModels::PLECS; else { throw std::runtime_error("Input JSON does not conform to schema!"); } } @@ -428,6 +430,7 @@ inline void to_json(json & j, const CircuitSimulatorExporterModels & x) { case CircuitSimulatorExporterModels::NGSPICE: j = "NgSpice"; break; case CircuitSimulatorExporterModels::LTSPICE: j = "LtSpice"; break; case CircuitSimulatorExporterModels::NL5: j = "NL5"; break; + case CircuitSimulatorExporterModels::PLECS: j = "PLECS"; break; default: throw std::runtime_error("Unexpected value in enumeration \"[object Object]\": " + std::to_string(static_cast(x))); } } @@ -558,6 +561,48 @@ class CircuitSimulatorExporterLtspiceModel : public CircuitSimulatorExporterMode std::string export_magnetic_as_symbol(Magnetic magnetic, std::optional filePathOrFile = std::nullopt); }; +class CircuitSimulatorExporterPlecsModel : public CircuitSimulatorExporterModel { + public: + std::string programName = "PLECS"; + double _modelSize = 400; + size_t _precision = 6; + + CircuitSimulatorExporterPlecsModel() = default; + + std::string export_magnetic_as_symbol(Magnetic magnetic, std::optional filePathOrFile = std::nullopt); + std::string export_magnetic_as_subcircuit(Magnetic magnetic, double frequency = defaults.measurementFrequency, double temperature = defaults.ambientTemperature, std::optional filePathOrFile = std::nullopt, CircuitSimulatorExporterCurveFittingModes mode = CircuitSimulatorExporterCurveFittingModes::LADDER); + + private: + std::string encode_init_commands(const std::string& plainText); + std::string emit_header(const std::string& name); + std::string emit_footer(); + std::string emit_schematic_header(); + std::string emit_schematic_footer(); + std::string emit_magnetic_interface(const std::string& name, const std::string& turnsVar, int polarity, std::vector position, const std::string& direction, bool flipped); + std::string emit_p_sat(const std::string& name, const std::string& areaVar, const std::string& lengthVar, const std::string& muRVar, const std::string& bSatVar, std::vector position, const std::string& direction, bool flipped); + std::string emit_p_air(const std::string& name, const std::string& areaVar, const std::string& lengthVar, std::vector position, const std::string& direction, bool flipped); + std::string emit_ac_voltage_source(const std::string& name, std::vector position); + std::string emit_resistor(const std::string& name, const std::string& valueVar, std::vector position); + std::string emit_scope(const std::string& name, std::vector position); + std::string emit_probe(const std::string& name, std::vector position, const std::vector>& probes); + std::string emit_connection(const std::string& type, const std::string& srcComponent, int srcTerminal, const std::string& dstComponent, int dstTerminal, std::vector> points = {}); + + // Shared helpers for subcircuit/symbol export + void build_physical_init(std::ostringstream& init, Core& core, + const std::vector& windings, + const std::vector& columns, + double temperature); + void build_magnetic_schematic(std::ostringstream& init, std::ostringstream& schematic, + Core& core, + const std::vector& windings, + const std::vector& columns, + bool isMultiColumn, int yBase); + void build_electrical_schematic(std::ostringstream& schematic, + const std::vector& windings, + bool isMultiColumn, int yBase); + std::string assemble_plecs_file(const std::string& name, const std::string& initStr, const std::string& schematicStr); +}; + class CircuitSimulationReader { public: enum class DataType : int { diff --git a/src/processors/CircuitSimulatorPlecs.cpp b/src/processors/CircuitSimulatorPlecs.cpp new file mode 100644 index 0000000..4ece405 --- /dev/null +++ b/src/processors/CircuitSimulatorPlecs.cpp @@ -0,0 +1,699 @@ +#include "processors/CircuitSimulatorInterface.h" +#include "physical_models/WindingLosses.h" +#include +#include +#include +#include + +namespace OpenMagnetics { + +// Base64 encoding for PLECS InitializationCommands +std::string CircuitSimulatorExporterPlecsModel::encode_init_commands(const std::string& plainText) { + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string encoded; + encoded.reserve(((plainText.size() + 2) / 3) * 4); + + for (size_t i = 0; i < plainText.size(); i += 3) { + uint32_t n = static_cast(plainText[i]) << 16; + if (i + 1 < plainText.size()) n |= static_cast(plainText[i + 1]) << 8; + if (i + 2 < plainText.size()) n |= static_cast(plainText[i + 2]); + + encoded += table[(n >> 18) & 0x3F]; + encoded += table[(n >> 12) & 0x3F]; + encoded += (i + 1 < plainText.size()) ? table[(n >> 6) & 0x3F] : '='; + encoded += (i + 2 < plainText.size()) ? table[n & 0x3F] : '='; + } + + // Split into 76-char lines with PLECS quoting + std::string result; + bool first = true; + for (size_t i = 0; i < encoded.size(); i += 76) { + std::string chunk = encoded.substr(i, 76); + if (first) { + result += " InitializationCommands base64 \"" + chunk + "\"\n"; + first = false; + } else { + result += "\"" + chunk + "\"\n"; + } + } + return result; +} + +std::string CircuitSimulatorExporterPlecsModel::emit_header(const std::string& name) { + std::ostringstream ss; + ss << "Plecs {\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Version \"4.7\"\n"; + ss << " CircuitModel \"ContStateSpace\"\n"; + ss << " StartTime \"0.0\"\n"; + ss << " TimeSpan \"tsim\"\n"; + ss << " Timeout \"\"\n"; + ss << " Solver \"dopri\"\n"; + ss << " MaxStep \"1\"\n"; + ss << " InitStep \"1\"\n"; + ss << " FixedStep \"1e-3\"\n"; + ss << " Refine \"1\"\n"; + ss << " ZCStepSize \"1e-9\"\n"; + ss << " RelTol \"1e-3\"\n"; + ss << " AbsTol \"-1\"\n"; + ss << " TurnOnThreshold \"0\"\n"; + ss << " SyncFixedStepTasks \"2\"\n"; + ss << " UseSingleCommonBaseRate \"2\"\n"; + ss << " LossVariableLimitExceededMsg \"3\"\n"; + ss << " NegativeSwitchLossMsg \"2\"\n"; + ss << " DivisionByZeroMsg \"2\"\n"; + ss << " StiffnessDetectionMsg \"2\"\n"; + ss << " MaxConsecutiveZCs \"10000\"\n"; + ss << " AlgebraicLoopWithStateMachineMsg \"2\"\n"; + ss << " AssertionAction \"1\"\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_footer() { + std::ostringstream ss; + ss << " InitialState \"1\"\n"; + ss << " SystemState \"\"\n"; + ss << " TaskingMode \"1\"\n"; + ss << " TaskConfigurations \"\"\n"; + ss << " CodeGenParameterInlining \"2\"\n"; + ss << " CodeGenFloatingPointFormat \"2\"\n"; + ss << " CodeGenAbsTimeUsageMsg \"3\"\n"; + ss << " CodeGenBaseName \"\"\n"; + ss << " CodeGenOutputDir \"\"\n"; + ss << " CodeGenExtraOpts \"\"\n"; + ss << " CodeGenTarget \"Generic\"\n"; + ss << " CodeGenTargetSettings \"\"\n"; + ss << " ExtendedMatrixPrecision \"1\"\n"; + ss << " MatrixSignificanceCheck \"2\"\n"; + ss << " EnableStateSpaceSplitting \"2\"\n"; + ss << " DisplayStateSpaceSplitting \"1\"\n"; + ss << " DiscretizationMethod \"2\"\n"; + ss << " ExternalModeSettings \"\"\n"; + ss << " AlgebraicLoopMethod \"1\"\n"; + ss << " AlgebraicLoopTolerance \"1e-6\"\n"; + ss << " ScriptsDialogGeometry \"\"\n"; + ss << " ScriptsDialogSplitterPos \"0\"\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_schematic_header() { + std::ostringstream ss; + ss << " Schematic {\n"; + ss << " Location [400, 200; 1300, 900]\n"; + ss << " ZoomFactor 1\n"; + ss << " SliderPosition [0, 0]\n"; + ss << " ShowBrowser off\n"; + ss << " BrowserWidth 100\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_schematic_footer() { + return " }\n}\n"; +} + +std::string CircuitSimulatorExporterPlecsModel::emit_magnetic_interface( + const std::string& name, const std::string& turnsVar, int polarity, + std::vector position, const std::string& direction, bool flipped) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type MagneticInterface\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show off\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction " << direction << "\n"; + ss << " Flipped " << (flipped ? "on" : "off") << "\n"; + ss << " LabelPosition east\n"; + ss << " Parameter {\n"; + ss << " Variable \"n\"\n"; + ss << " Value \"" << turnsVar << "\"\n"; + ss << " Show off\n"; + ss << " }\n"; + ss << " Parameter {\n"; + ss << " Variable \"Polarity\"\n"; + ss << " Value \"" << polarity << "\"\n"; + ss << " Show off\n"; + ss << " }\n"; + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_p_sat( + const std::string& name, const std::string& areaVar, const std::string& lengthVar, + const std::string& muRVar, const std::string& bSatVar, + std::vector position, const std::string& direction, bool flipped) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type Reference\n"; + ss << " SrcComponent \"Components/Magnetic/Components/P_sat\"\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show off\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction " << direction << "\n"; + ss << " Flipped " << (flipped ? "on" : "off") << "\n"; + ss << " LabelPosition east\n"; + ss << " Frame [-8, -15; 8, 15]\n"; + ss << " Parameter {\n Variable \"fitting\"\n Value \"1\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"A\"\n Value \"" << areaVar << "\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"l\"\n Value \"" << lengthVar << "\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"mu_r_unsat\"\n Value \"" << muRVar << "\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"mu_r_sat\"\n Value \"1\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"B_sat\"\n Value \"" << bSatVar << "\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"F_init\"\n Value \"0\"\n Show off\n }\n"; + ss << " Terminal {\n Type MagneticPort\n Position [0, -20]\n Direction up\n }\n"; + ss << " Terminal {\n Type MagneticPort\n Position [0, 20]\n Direction down\n }\n"; + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_p_air( + const std::string& name, const std::string& areaVar, const std::string& lengthVar, + std::vector position, const std::string& direction, bool flipped) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type Reference\n"; + ss << " SrcComponent \"Components/Magnetic/Components/P_air\"\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show off\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction " << direction << "\n"; + ss << " Flipped " << (flipped ? "on" : "off") << "\n"; + ss << " LabelPosition east\n"; + ss << " Frame [-8, -10; 8, 10]\n"; + ss << " Parameter {\n Variable \"A\"\n Value \"" << areaVar << "\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"l\"\n Value \"" << lengthVar << "\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"F_init\"\n Value \"0\"\n Show off\n }\n"; + ss << " Terminal {\n Type MagneticPort\n Position [0, -15]\n Direction up\n }\n"; + ss << " Terminal {\n Type MagneticPort\n Position [0, 15]\n Direction down\n }\n"; + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_ac_voltage_source( + const std::string& name, std::vector position) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type ACVoltageSource\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show on\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction down\n"; + ss << " Flipped on\n"; + ss << " LabelPosition east\n"; + ss << " Parameter {\n Variable \"V\"\n Value \"V_p\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"w\"\n Value \"2*pi*f_sin\"\n Show off\n }\n"; + ss << " Parameter {\n Variable \"phi\"\n Value \"0\"\n Show off\n }\n"; + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_resistor( + const std::string& name, const std::string& valueVar, std::vector position) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type Resistor\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show on\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction up\n"; + ss << " Flipped off\n"; + ss << " LabelPosition east\n"; + ss << " Parameter {\n Variable \"R\"\n Value \"" << valueVar << "\"\n Show off\n }\n"; + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_scope( + const std::string& name, std::vector position) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type Scope\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show on\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction up\n"; + ss << " Flipped off\n"; + ss << " LabelPosition south\n"; + ss << " Location [588, 348; 938, 595]\n"; + ss << " State \"\"\n"; + ss << " SavedViews \"\"\n"; + ss << " HeaderState \"\"\n"; + ss << " PlotPalettes \"\"\n"; + ss << " Axes \"1\"\n"; + ss << " TimeRange \"0.0\"\n"; + ss << " ScrollingMode \"1\"\n"; + ss << " SingleTimeAxis \"1\"\n"; + ss << " Open \"0\"\n"; + ss << " Ts \"-1\"\n"; + ss << " SampleLimit \"0\"\n"; + ss << " XAxisLabel \"Time / s\"\n"; + ss << " ShowLegend \"1\"\n"; + ss << " Axis {\n"; + ss << " Name \"\"\n"; + ss << " AutoScale 1\n"; + ss << " MinValue 0\n"; + ss << " MaxValue 1\n"; + ss << " Signals {}\n"; + ss << " SignalTypes [ ]\n"; + ss << " Untangle 0\n"; + ss << " KeepBaseline off\n"; + ss << " BaselineValue 0\n"; + ss << " }\n"; + ss << " Fourier {\n"; + ss << " SingleXAxis on\n"; + ss << " AxisLabel \"Frequency / Hz\"\n"; + ss << " Scaling 0\n"; + ss << " PhaseDisplay 0\n"; + ss << " ShowFourierLegend off\n"; + ss << " Axis {\n"; + ss << " Name \"\"\n"; + ss << " AutoScale 1\n"; + ss << " MinValue 0\n"; + ss << " MaxValue 1\n"; + ss << " Signals {}\n"; + ss << " Untangle 0\n"; + ss << " KeepBaseline off\n"; + ss << " BaselineValue 0\n"; + ss << " }\n"; + ss << " }\n"; + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_probe( + const std::string& name, std::vector position, + const std::vector>& probes) { + std::ostringstream ss; + ss << " Component {\n"; + ss << " Type PlecsProbe\n"; + ss << " Name \"" << name << "\"\n"; + ss << " Show on\n"; + ss << " Position [" << position[0] << ", " << position[1] << "]\n"; + ss << " Direction right\n"; + ss << " Flipped off\n"; + ss << " LabelPosition south\n"; + for (const auto& [component, signal] : probes) { + ss << " Probe {\n"; + ss << " Component \"" << component << "\"\n"; + ss << " Path \"\"\n"; + ss << " Signals {\"" << signal << "\"}\n"; + ss << " }\n"; + } + ss << " }\n"; + return ss.str(); +} + +std::string CircuitSimulatorExporterPlecsModel::emit_connection( + const std::string& type, + const std::string& srcComponent, int srcTerminal, + const std::string& dstComponent, int dstTerminal, + std::vector> points) { + std::ostringstream ss; + ss << " Connection {\n"; + ss << " Type " << type << "\n"; + ss << " SrcComponent \"" << srcComponent << "\"\n"; + ss << " SrcTerminal " << srcTerminal << "\n"; + if (!points.empty()) { + ss << " Points "; + for (size_t i = 0; i < points.size(); ++i) { + ss << "[" << points[i][0] << ", " << points[i][1] << "]"; + if (i < points.size() - 1) ss << "; "; + } + ss << "\n"; + } + ss << " DstComponent \"" << dstComponent << "\"\n"; + ss << " DstTerminal " << dstTerminal << "\n"; + ss << " }\n"; + return ss.str(); +} + +void CircuitSimulatorExporterPlecsModel::build_physical_init( + std::ostringstream& init, Core& core, + const std::vector& windings, + const std::vector& columns, + double temperature) { + + size_t numWindings = windings.size(); + init << "mu_0 = 4*pi*1e-7;\n\n"; + + // Winding turns + for (size_t i = 0; i < numWindings; ++i) { + std::string varName = (numWindings == 1) ? "n_turns" : "n_w" + std::to_string(i); + init << varName << " = " << windings[i].get_number_turns() << ";\n"; + } + init << "\n"; + + // Core parameters + double mu_r = core.get_initial_permeability(temperature); + init << "mu_r = " << mu_r << ";\n"; + double bSat = core.get_magnetic_flux_density_saturation(temperature, false); + init << "B_sat = " << bSat << ";\n\n"; + + bool isMultiColumn = columns.size() > 1; + if (isMultiColumn) { + for (size_t ci = 0; ci < columns.size(); ++ci) { + auto& col = columns[ci]; + std::string suffix; + if (col.get_coordinates()[0] == 0) suffix = "_center"; + else if (col.get_coordinates()[0] < 0) suffix = "_left"; + else suffix = "_right"; + init << "A" << suffix << " = " << col.get_area() << ";\n"; + init << "l" << suffix << " = " << col.get_height() << ";\n"; + } + double lPlate = core.get_effective_length(); + for (auto& col : columns) lPlate -= col.get_height(); + lPlate = std::max(lPlate / 2.0, 1e-6); + init << "l_plate = " << lPlate << ";\n"; + init << "l_outer_total = l_left + 2*l_plate;\n"; + } else { + init << "A_e = " << core.get_effective_area() << ";\n"; + init << "l_e = " << core.get_effective_length() << ";\n"; + } + init << "\n"; + + // Air gap parameters per column + for (size_t ci = 0; ci < columns.size(); ++ci) { + auto gapsInColumn = core.find_gaps_by_column(columns[ci]); + for (size_t gi = 0; gi < gapsInColumn.size(); ++gi) { + if (gapsInColumn[gi].get_length() <= 0) continue; + std::string gapId = "gap_c" + std::to_string(ci) + "_g" + std::to_string(gi); + init << "l_" << gapId << " = " << gapsInColumn[gi].get_length() << ";\n"; + double gapArea = gapsInColumn[gi].get_area().has_value() + ? gapsInColumn[gi].get_area().value() + : core.get_effective_area(); + init << "A_" << gapId << " = " << gapArea << ";\n"; + } + } + init << "\n"; +} + +void CircuitSimulatorExporterPlecsModel::build_magnetic_schematic( + std::ostringstream& init, std::ostringstream& schematic, + Core& core, + const std::vector& windings, + const std::vector& columns, + bool isMultiColumn, int yBase) { + + const int magX = 440; + const int pSatX = 405; + const int ySpacing = 60; + size_t numWindings = windings.size(); + + if (!isMultiColumn) { + // ============ SINGLE-COLUMN TOPOLOGY ============ + int primaryIndex = 0; + int secondaryIndex = 0; + for (size_t i = 0; i < numWindings; ++i) { + std::string miName = "MagInt_w" + std::to_string(i); + std::string turnsVar = (numWindings == 1) ? "n_turns" : "n_w" + std::to_string(i); + if (windings[i].get_isolation_side() == IsolationSide::PRIMARY) { + int yPos = yBase + primaryIndex * ySpacing; + schematic << emit_magnetic_interface(miName, turnsVar, 1, {magX, yPos}, "up", false); + ++primaryIndex; + } else { + int yPos = yBase - (secondaryIndex + 1) * ySpacing; + schematic << emit_magnetic_interface(miName, turnsVar, 1, {magX, yPos}, "down", true); + ++secondaryIndex; + } + } + + schematic << emit_p_sat("P_satCore", "A_e", "l_e", "mu_r", "B_sat", + {pSatX, yBase - 50}, "up", false); + + auto gapsInColumn = core.find_gaps_by_column(columns[0]); + int gapY = yBase + static_cast(numWindings) * ySpacing + 10; + int gapCount = 0; + for (size_t gi = 0; gi < gapsInColumn.size(); ++gi) { + if (gapsInColumn[gi].get_length() <= 0) continue; + std::string gapId = "gap_c0_g" + std::to_string(gi); + schematic << emit_p_air("P_air_" + gapId, "A_" + gapId, "l_" + gapId, + {magX, gapY + gapCount * 40}, "up", true); + gapCount++; + } + if (gapCount == 0) { + init << "l_air_equiv = l_e;\nA_air_equiv = A_e;\n"; + schematic << emit_p_air("P_airEquiv", "A_air_equiv", "l_air_equiv", + {magX, gapY}, "up", true); + gapCount = 1; + } + + // Magnetic connections + for (size_t i = 0; i + 1 < numWindings; ++i) { + schematic << emit_connection("Magnetic", "MagInt_w" + std::to_string(i), 3, + "MagInt_w" + std::to_string(i + 1), 4); + } + std::string topMI = (numWindings > 1) ? "MagInt_w" + std::to_string(numWindings - 1) : "MagInt_w0"; + int topMagY = yBase - 80; + schematic << emit_connection("Magnetic", "P_satCore", 1, topMI, 3, + {{pSatX, topMagY}, {magX, topMagY}}); + std::string lastGap = (gapCount > 0 && !gapsInColumn.empty() && gapsInColumn[0].get_length() > 0) + ? "P_air_gap_c0_g" + std::to_string(gapsInColumn.size() - 1) + : "P_airEquiv"; + schematic << emit_connection("Magnetic", lastGap, 2, "MagInt_w0", 4); + std::string firstGap = (!gapsInColumn.empty() && gapsInColumn[0].get_length() > 0) + ? "P_air_gap_c0_g0" : "P_airEquiv"; + int bottomY = gapY + (gapCount - 1) * 40 + 30; + schematic << emit_connection("Magnetic", firstGap, 1, "P_satCore", 2, + {{magX, bottomY}, {pSatX, bottomY}}); + } else { + // ============ MULTI-COLUMN (E-CORE) TOPOLOGY ============ + int outerX_L = 310, outerX_R = 570; + + size_t centerColIdx = 0; + for (size_t ci = 0; ci < columns.size(); ++ci) { + if (columns[ci].get_coordinates()[0] == 0) { centerColIdx = ci; break; } + } + + int primaryIndex = 0; + int secondaryIndex = 0; + for (size_t i = 0; i < numWindings; ++i) { + std::string miName = "MagInt_w" + std::to_string(i); + std::string turnsVar = "n_w" + std::to_string(i); + if (windings[i].get_isolation_side() == IsolationSide::PRIMARY) { + int yPos = yBase + primaryIndex * ySpacing; + schematic << emit_magnetic_interface(miName, turnsVar, 1, {magX, yPos}, "up", false); + ++primaryIndex; + } else { + int yPos = yBase - (secondaryIndex + 1) * ySpacing; + schematic << emit_magnetic_interface(miName, turnsVar, 1, {magX, yPos}, "down", true); + ++secondaryIndex; + } + } + + schematic << emit_p_sat("P_satCenter", "A_center", "l_center", "mu_r", "B_sat", + {magX, yBase + static_cast(numWindings) * ySpacing + 20}, "up", false); + + auto centerGaps = core.find_gaps_by_column(columns[centerColIdx]); + int centerGapCount = 0; + int centerGapY = yBase - 20; + for (size_t gi = 0; gi < centerGaps.size(); ++gi) { + if (centerGaps[gi].get_length() <= 0) continue; + std::string gapId = "gap_c" + std::to_string(centerColIdx) + "_g" + std::to_string(gi); + schematic << emit_p_air("P_air_" + gapId, "A_" + gapId, "l_" + gapId, + {magX, centerGapY - centerGapCount * 40}, "up", true); + centerGapCount++; + } + if (centerGapCount == 0) { + init << "l_no_gap = 1e-6;\nA_no_gap = A_center;\n"; + schematic << emit_p_air("P_air_no_gap", "A_no_gap", "l_no_gap", + {magX, centerGapY}, "up", true); + centerGapCount = 1; + } + + schematic << emit_p_sat("P_satOuterL", "A_left", "l_outer_total", "mu_r", "B_sat", + {outerX_L, yBase + 50}, "up", false); + schematic << emit_p_sat("P_satOuterR", "A_right", "l_outer_total", "mu_r", "B_sat", + {outerX_R, yBase + 50}, "up", false); + + // Determine first/last gap names for connections + std::string firstCenterGap, lastCenterGap; + if (centerGapCount > 0 && !centerGaps.empty() && centerGaps[0].get_length() > 0) { + firstCenterGap = "P_air_gap_c" + std::to_string(centerColIdx) + "_g0"; + size_t lastGapIdx = 0; + for (size_t gi = 0; gi < centerGaps.size(); ++gi) { + if (centerGaps[gi].get_length() > 0) lastGapIdx = gi; + } + lastCenterGap = "P_air_gap_c" + std::to_string(centerColIdx) + "_g" + std::to_string(lastGapIdx); + } else { + firstCenterGap = "P_air_no_gap"; + lastCenterGap = "P_air_no_gap"; + } + + // Chain gaps in series if multiple + for (int g = 0; g + 1 < centerGapCount; ++g) { + std::string gapA = "P_air_gap_c" + std::to_string(centerColIdx) + "_g" + std::to_string(g); + std::string gapB = "P_air_gap_c" + std::to_string(centerColIdx) + "_g" + std::to_string(g + 1); + schematic << emit_connection("Magnetic", gapA, 1, gapB, 2); + } + + // Magnetic connections + for (size_t i = 0; i + 1 < numWindings; ++i) { + schematic << emit_connection("Magnetic", "MagInt_w" + std::to_string(i), 3, + "MagInt_w" + std::to_string(i + 1), 4); + } + std::string topMI = (numWindings > 1) ? "MagInt_w" + std::to_string(numWindings - 1) : "MagInt_w0"; + schematic << emit_connection("Magnetic", lastCenterGap, 2, topMI, 3); + schematic << emit_connection("Magnetic", "P_satCenter", 1, "MagInt_w0", 4); + int topNodeY = yBase - 50; + schematic << emit_connection("Magnetic", firstCenterGap, 1, "P_satOuterL", 1, + {{magX, topNodeY}, {outerX_L, topNodeY}}); + schematic << emit_connection("Magnetic", firstCenterGap, 1, "P_satOuterR", 1, + {{magX, topNodeY}, {outerX_R, topNodeY}}); + int bottomNodeY = yBase + static_cast(numWindings) * ySpacing + 60; + schematic << emit_connection("Magnetic", "P_satCenter", 2, "P_satOuterL", 2, + {{magX, bottomNodeY}, {outerX_L, bottomNodeY}}); + schematic << emit_connection("Magnetic", "P_satCenter", 2, "P_satOuterR", 2, + {{magX, bottomNodeY}, {outerX_R, bottomNodeY}}); + } +} + +void CircuitSimulatorExporterPlecsModel::build_electrical_schematic( + std::ostringstream& schematic, + const std::vector& windings, + bool isMultiColumn, int yBase) { + + const int magX = 440; + const int vSrcX = 205; + const int loadX = 635; + const int scopeX = 720; + const int probeX = 660; + const int ySpacing = 60; + size_t numWindings = windings.size(); + + schematic << emit_ac_voltage_source("V_ac", {vSrcX, yBase}); + + // Load resistor per secondary winding + int secCount = 0; + for (size_t i = 0; i < numWindings; ++i) { + if (windings[i].get_isolation_side() == IsolationSide::PRIMARY) continue; + std::string rName = (secCount == 0) ? "R_load" : "R_load_" + std::to_string(secCount); + int secY = yBase - static_cast(i) * ySpacing + 30; + schematic << emit_resistor(rName, "RL", {loadX, secY}); + secCount++; + } + + // Scope + Probe + int scopeY = isMultiColumn ? yBase - 160 : yBase - 120; + schematic << emit_scope("Scope", {scopeX, scopeY}); + std::vector> probeSignals = {{"V_ac", "Source voltage"}}; + secCount = 0; + for (size_t i = 0; i < numWindings; ++i) { + if (windings[i].get_isolation_side() == IsolationSide::PRIMARY) continue; + std::string rName = (secCount == 0) ? "R_load" : "R_load_" + std::to_string(secCount); + probeSignals.push_back({rName, "Resistor voltage " + std::to_string(secCount)}); + secCount++; + } + schematic << emit_probe("Probe", {probeX, scopeY}, probeSignals); + + // Wire connections + secCount = 0; + for (size_t i = 0; i < numWindings; ++i) { + std::string miName = "MagInt_w" + std::to_string(i); + if (windings[i].get_isolation_side() == IsolationSide::PRIMARY) { + schematic << emit_connection("Wire", miName, 1, "V_ac", 1, + {{magX - 75, yBase - 15}, {vSrcX, yBase - 15}}); + schematic << emit_connection("Wire", miName, 2, "V_ac", 2, + {{magX - 75, yBase + 15}, {vSrcX, yBase + 15}}); + } else { + std::string rName = (secCount == 0) ? "R_load" : "R_load_" + std::to_string(secCount); + int secY = yBase - static_cast(i) * ySpacing; + schematic << emit_connection("Wire", miName, 1, rName, 1, + {{magX + 65, secY - 15}, {loadX, secY - 15}}); + schematic << emit_connection("Wire", miName, 2, rName, 2, + {{magX + 65, secY + 15}, {loadX, secY + 15}}); + secCount++; + } + } + + schematic << emit_connection("Signal", "Probe", 1, "Scope", 1); +} + +std::string CircuitSimulatorExporterPlecsModel::assemble_plecs_file( + const std::string& name, const std::string& initStr, const std::string& schematicStr) { + std::string result; + result += emit_header(name); + result += encode_init_commands(initStr); + result += emit_footer(); + result += schematicStr; + return result; +} + +std::string CircuitSimulatorExporterPlecsModel::export_magnetic_as_subcircuit( + Magnetic magnetic, double frequency, double temperature, + [[maybe_unused]] std::optional filePathOrFile, + [[maybe_unused]] CircuitSimulatorExporterCurveFittingModes mode) { + + auto core = magnetic.get_core(); + auto coil = magnetic.get_coil(); + auto columns = core.get_columns(); + auto windings = coil.get_functional_description(); + bool isMultiColumn = columns.size() > 1; + + std::string name = magnetic.get_reference(); + if (name.empty()) name = "magnetic"; + + // Build InitializationCommands + std::ostringstream init; + init << std::scientific << std::setprecision(_precision); + init << "%% PLECS model generated by OpenMagnetics\n"; + init << "%% Component: " << name << "\n\n"; + init << "%% Simulation\n"; + init << "tsim = 0.01;\n\n"; + init << "%% Electrical\n"; + init << "V_p = 1;\n"; + init << "f_sin = " << frequency << ";\n"; + init << "RL = 1e3;\n\n"; + + build_physical_init(init, core, windings, columns, temperature); + + // DC resistance per winding + for (size_t i = 0; i < windings.size(); ++i) { + double dcR = WindingLosses::calculate_effective_resistance_of_winding(magnetic, i, 0.1, temperature); + init << "R_dc_w" << i << " = " << dcR << ";\n"; + } + + // Build schematic + std::ostringstream schematic; + schematic << emit_schematic_header(); + + int yBase = isMultiColumn ? 390 : 290; + build_magnetic_schematic(init, schematic, core, windings, columns, isMultiColumn, yBase); + build_electrical_schematic(schematic, windings, isMultiColumn, yBase); + + schematic << emit_schematic_footer(); + + return assemble_plecs_file(name, init.str(), schematic.str()); +} + +std::string CircuitSimulatorExporterPlecsModel::export_magnetic_as_symbol( + Magnetic magnetic, [[maybe_unused]] std::optional filePathOrFile) { + + auto core = magnetic.get_core(); + auto coil = magnetic.get_coil(); + auto columns = core.get_columns(); + auto windings = coil.get_functional_description(); + bool isMultiColumn = columns.size() > 1; + + std::string name = magnetic.get_reference(); + if (name.empty()) name = "magnetic"; + + // Build InitializationCommands (no simulation/electrical params) + std::ostringstream init; + init << std::scientific << std::setprecision(_precision); + init << "%% PLECS subcircuit symbol generated by OpenMagnetics\n"; + init << "%% Component: " << name << "\n\n"; + + build_physical_init(init, core, windings, columns, defaults.ambientTemperature); + + // Build schematic — magnetic circuit only + std::ostringstream schematic; + schematic << emit_schematic_header(); + + int yBase = isMultiColumn ? 390 : 290; + build_magnetic_schematic(init, schematic, core, windings, columns, isMultiColumn, yBase); + + schematic << emit_schematic_footer(); + + return assemble_plecs_file(name, init.str(), schematic.str()); +} + +} // namespace OpenMagnetics diff --git a/tests/TestCircuitSimulatorPlecs.cpp b/tests/TestCircuitSimulatorPlecs.cpp new file mode 100644 index 0000000..4421ba1 --- /dev/null +++ b/tests/TestCircuitSimulatorPlecs.cpp @@ -0,0 +1,221 @@ +#include "processors/CircuitSimulatorInterface.h" +#include "TestingUtils.h" +#include +#include +#include + +using namespace MAS; +using namespace OpenMagnetics; + +namespace { +auto outputFilePath = std::filesystem::path{__FILE__}.parent_path().append("..").append("output"); + +struct EnsureOutputDir { + EnsureOutputDir() { std::filesystem::create_directories(outputFilePath); } +}; +static EnsureOutputDir _ensureOutputDir; + +TEST_CASE("Test_CircuitSimulatorExporter_Plecs_Simple_Inductor", "[processor][circuit-simulator-exporter][plecs]") { + std::vector numberTurns = {20}; + std::vector numberParallels = {1}; + std::string shapeName = "E 32/16/9"; + + auto coil = OpenMagneticsTesting::get_quick_coil(numberTurns, numberParallels, shapeName); + int64_t numberStacks = 1; + std::string coreMaterial = "N87"; + auto gapping = OpenMagneticsTesting::get_ground_gap(0.001); + auto core = OpenMagneticsTesting::get_quick_core(shapeName, gapping, numberStacks, coreMaterial); + OpenMagnetics::Magnetic magnetic; + magnetic.set_core(core); + magnetic.set_coil(coil); + + auto plecsFile = outputFilePath; + plecsFile.append("Test_Plecs_Simple_Inductor.plecs"); + std::filesystem::remove(plecsFile); + + CircuitSimulatorExporter exporter(CircuitSimulatorExporterModels::PLECS); + exporter.export_magnetic_as_subcircuit(magnetic, 100000, 25, plecsFile.string()); + + REQUIRE(std::filesystem::exists(plecsFile)); + + std::ifstream f(plecsFile); + std::string content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + + REQUIRE(content.find("Plecs {") != std::string::npos); + REQUIRE(content.find("MagneticInterface") != std::string::npos); + REQUIRE(content.find("P_sat") != std::string::npos); + REQUIRE(content.find("P_air") != std::string::npos); + REQUIRE(content.find("InitializationCommands") != std::string::npos); + REQUIRE(content.find("ACVoltageSource") != std::string::npos); +} + +TEST_CASE("Test_CircuitSimulatorExporter_Plecs_Transformer", "[processor][circuit-simulator-exporter][plecs]") { + std::vector numberTurns = {24, 12}; + std::vector numberParallels = {1, 1}; + std::string shapeName = "E 42/21/15"; + + auto coil = OpenMagneticsTesting::get_quick_coil(numberTurns, numberParallels, shapeName); + int64_t numberStacks = 1; + std::string coreMaterial = "N87"; + auto gapping = OpenMagneticsTesting::get_ground_gap(0.0001); + auto core = OpenMagneticsTesting::get_quick_core(shapeName, gapping, numberStacks, coreMaterial); + OpenMagnetics::Magnetic magnetic; + magnetic.set_core(core); + magnetic.set_coil(coil); + + auto plecsFile = outputFilePath; + plecsFile.append("Test_Plecs_Transformer.plecs"); + std::filesystem::remove(plecsFile); + + CircuitSimulatorExporter exporter(CircuitSimulatorExporterModels::PLECS); + exporter.export_magnetic_as_subcircuit(magnetic, 100000, 25, plecsFile.string()); + + REQUIRE(std::filesystem::exists(plecsFile)); + + std::ifstream f(plecsFile); + std::string content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + + // Transformer should have two MagneticInterface components + size_t firstMagInt = content.find("MagneticInterface"); + REQUIRE(firstMagInt != std::string::npos); + size_t secondMagInt = content.find("MagneticInterface", firstMagInt + 1); + REQUIRE(secondMagInt != std::string::npos); + + // Should have a Resistor (load) + REQUIRE(content.find("Resistor") != std::string::npos); +} + +TEST_CASE("Test_CircuitSimulatorExporter_Plecs_Symbol", "[processor][circuit-simulator-exporter][plecs]") { + std::vector numberTurns = {24, 12}; + std::vector numberParallels = {1, 1}; + std::string shapeName = "E 42/21/15"; + + auto coil = OpenMagneticsTesting::get_quick_coil(numberTurns, numberParallels, shapeName); + int64_t numberStacks = 1; + std::string coreMaterial = "N87"; + auto gapping = OpenMagneticsTesting::get_ground_gap(0.0001); + auto core = OpenMagneticsTesting::get_quick_core(shapeName, gapping, numberStacks, coreMaterial); + OpenMagnetics::Magnetic magnetic; + magnetic.set_core(core); + magnetic.set_coil(coil); + + auto plecsFile = outputFilePath; + plecsFile.append("Test_Plecs_Symbol.plecs"); + std::filesystem::remove(plecsFile); + + CircuitSimulatorExporter exporter(CircuitSimulatorExporterModels::PLECS); + exporter.export_magnetic_as_symbol(magnetic, plecsFile.string()); + + REQUIRE(std::filesystem::exists(plecsFile)); + + std::ifstream f(plecsFile); + std::string content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + + // Symbol should have magnetic components but no test circuit + REQUIRE(content.find("Plecs {") != std::string::npos); + REQUIRE(content.find("MagneticInterface") != std::string::npos); + REQUIRE(content.find("P_sat") != std::string::npos); + REQUIRE(content.find("ACVoltageSource") == std::string::npos); + REQUIRE(content.find("Scope") == std::string::npos); +} + +TEST_CASE("Test_CircuitSimulatorExporter_Plecs_BSat_From_Material", "[processor][circuit-simulator-exporter][plecs]") { + std::vector numberTurns = {20}; + std::vector numberParallels = {1}; + std::string shapeName = "E 32/16/9"; + + auto coil = OpenMagneticsTesting::get_quick_coil(numberTurns, numberParallels, shapeName); + int64_t numberStacks = 1; + std::string coreMaterial = "N87"; + auto gapping = OpenMagneticsTesting::get_ground_gap(0.001); + auto core = OpenMagneticsTesting::get_quick_core(shapeName, gapping, numberStacks, coreMaterial); + OpenMagnetics::Magnetic magnetic; + magnetic.set_core(core); + magnetic.set_coil(coil); + + CircuitSimulatorExporter exporter(CircuitSimulatorExporterModels::PLECS); + auto content = exporter.export_magnetic_as_subcircuit(magnetic, 100000, 25); + + // B_sat should come from material data, not be hardcoded to 0.49 + // N87 at 25C has B_sat around 0.49 but should be computed, not literal + REQUIRE(content.find("Plecs {") != std::string::npos); + REQUIRE(content.find("InitializationCommands") != std::string::npos); +} + +TEST_CASE("Test_CircuitSimulatorExporter_Plecs_Transformer_3_Windings", "[processor][circuit-simulator-exporter][plecs]") { + std::vector numberTurns = {24, 12, 6}; + std::vector numberParallels = {1, 1, 1}; + std::string shapeName = "E 42/21/15"; + + auto coil = OpenMagneticsTesting::get_quick_coil(numberTurns, numberParallels, shapeName); + int64_t numberStacks = 1; + std::string coreMaterial = "N87"; + auto gapping = OpenMagneticsTesting::get_ground_gap(0.0001); + auto core = OpenMagneticsTesting::get_quick_core(shapeName, gapping, numberStacks, coreMaterial); + OpenMagnetics::Magnetic magnetic; + magnetic.set_core(core); + magnetic.set_coil(coil); + + auto plecsFile = outputFilePath; + plecsFile.append("Test_Plecs_Transformer_3_Windings.plecs"); + std::filesystem::remove(plecsFile); + + CircuitSimulatorExporter exporter(CircuitSimulatorExporterModels::PLECS); + exporter.export_magnetic_as_subcircuit(magnetic, 100000, 25, plecsFile.string()); + + REQUIRE(std::filesystem::exists(plecsFile)); + + std::ifstream f(plecsFile); + std::string content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + + // Should have three MagneticInterface components + size_t pos = 0; + int magIntCount = 0; + while ((pos = content.find("MagneticInterface", pos)) != std::string::npos) { + magIntCount++; + pos += 17; + } + REQUIRE(magIntCount >= 3); + + // Should have at least two Resistor components (one per secondary) + pos = 0; + int resistorCount = 0; + while ((pos = content.find("Resistor", pos)) != std::string::npos) { + resistorCount++; + pos += 8; + } + REQUIRE(resistorCount >= 2); +} + +TEST_CASE("Test_CircuitSimulatorExporter_Plecs_MultiColumn_Multiple_Gaps", "[processor][circuit-simulator-exporter][plecs]") { + std::vector numberTurns = {20}; + std::vector numberParallels = {1}; + std::string shapeName = "E 32/16/9"; + + auto coil = OpenMagneticsTesting::get_quick_coil(numberTurns, numberParallels, shapeName); + int64_t numberStacks = 1; + std::string coreMaterial = "N87"; + auto gapping = OpenMagneticsTesting::get_distributed_gap(0.0005, 3); + auto core = OpenMagneticsTesting::get_quick_core(shapeName, gapping, numberStacks, coreMaterial); + OpenMagnetics::Magnetic magnetic; + magnetic.set_core(core); + magnetic.set_coil(coil); + + auto plecsFile = outputFilePath; + plecsFile.append("Test_Plecs_MultiColumn_Multiple_Gaps.plecs"); + std::filesystem::remove(plecsFile); + + CircuitSimulatorExporter exporter(CircuitSimulatorExporterModels::PLECS); + exporter.export_magnetic_as_subcircuit(magnetic, 100000, 25, plecsFile.string()); + + REQUIRE(std::filesystem::exists(plecsFile)); + + std::ifstream f(plecsFile); + std::string content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + + // Should have multiple P_air components for distributed gaps + REQUIRE(content.find("P_air_") != std::string::npos); + REQUIRE(content.find("P_sat") != std::string::npos); +} + +} // namespace diff --git a/tests/testData/plecs/ecore_transformer.plecs b/tests/testData/plecs/ecore_transformer.plecs new file mode 100644 index 0000000..7823ddf --- /dev/null +++ b/tests/testData/plecs/ecore_transformer.plecs @@ -0,0 +1,537 @@ +Plecs { + Name "ecore_transformer" + Version "4.7" + CircuitModel "ContStateSpace" + StartTime "0.0" + TimeSpan "tsim" + Timeout "" + Solver "dopri" + MaxStep "1" + InitStep "1" + FixedStep "1e-3" + Refine "1" + ZCStepSize "1e-9" + RelTol "1e-3" + AbsTol "-1" + TurnOnThreshold "0" + SyncFixedStepTasks "2" + UseSingleCommonBaseRate "2" + LossVariableLimitExceededMsg "3" + NegativeSwitchLossMsg "2" + DivisionByZeroMsg "2" + StiffnessDetectionMsg "2" + MaxConsecutiveZCs "10000" + AlgebraicLoopWithStateMachineMsg "2" + AssertionAction "1" + InitializationCommands base64 "JSUgRmlsZSA0OiBlY29yZV90cmFuc2Zvcm1lci5wbGVjcwolJSBUb3BvbG9neTogRS1jb3JlIHRy" +"YW5zZm9ybWVyIHdpdGggZXhwbGljaXQgMy1sZWcgbWFnbmV0aWMgY2lyY3VpdAolJSAKJSUgUGFy" +"YW1ldGVyCVZhbHVlCiUlIENvcmUgc2hhcGUJRTQyLzIxLzE1CiUlIENvcmUgbWF0ZXJpYWwJTjg3" +"LCBtdV9yID0gMjIwMAolJSBDZW50ZXIgbGVnIGNyb3NzLXNlY3Rpb24JQV9jZW50ZXIgPSAxNzgg" +"bW1eMiA9IDE3OGUtNiBtXjIKJSUgQ2VudGVyIGxlZyBsZW5ndGgJbF9jZW50ZXIgPSA0MiBtbSA9" +"IDQyZS0zIG0KJSUgT3V0ZXIgbGVnIGNyb3NzLXNlY3Rpb24JQV9vdXRlciA9IDg5IG1tXjIgPSA4" +"OWUtNiBtXjIKJSUgT3V0ZXIgbGVnIGxlbmd0aAlsX291dGVyID0gNDJlLTMgbQolJSBUb3AvYm90" +"dG9tIHBsYXRlIGxlbmd0aAlsX3BsYXRlID0gMjcuNiBtbSA9IDI3LjZlLTMgbQolJSBUb3AvYm90" +"dG9tIHBsYXRlIGNyb3NzLXNlY3Rpb24JQV9wbGF0ZSA9IDg5ZS02IG1eMgolJSBDZW50ZXIgbGVn" +"IGFpciBnYXAJbF9nYXAgPSAxLjAgbW0sIEFfZ2FwID0gMTc4ZS02IG1eMgolJSBQcmltYXJ5IHdp" +"bmRpbmcJTjEgPSAyNCB0dXJucwolJSBTZWNvbmRhcnkgd2luZGluZwlOMiA9IDEyIHR1cm5zCgol" +"JSBTaW11bGF0aW9uCnRzaW0gPSAwLjE7ICAgICAgICAgICUgW3NdIC0gc2ltdWxhdGlvbiB0aW1l" +"CgolJSBFbGVjdHJpY2FsClZfcCA9IDE7ICAgICAgICAgICAgICUgW1ZdIC0gcGVhayBzaW5lCmZf" +"c2luID0gMTAwZTM7ICAgICAgICUgW0h6XSAtIGZyZXF1ZW5jeQpuX3ByaW0gPSAyNDsgICAgICAg" +"ICAlIFtdIC0gcHJpbWFyeSB0dXJucwpuX3NuZHJ5ID0gMTI7ICAgICAgICAlIFtdIC0gc2Vjb25k" +"YXJ5IHR1cm5zClJMID0gMWUzOyAgICAgICAgICAgICUgW09obV0gLSBsb2FkIHJlc2lzdGFuY2UK" +"CiUlIENvcmUgcGFyYW1ldGVycwptdV9yID0gMjIwMDsgICAgICAgICAlIFtdIC0gcmVsYXRpdmUg" +"cGVybWVhYmlsaXR5Cm11XzAgPSA0KnBpKjFlLTc7ICAgJSBbSC9tXSAtIHBlcm1lYWJpbGl0eSBv" +"ZiBmcmVlIHNwYWNlCkJfc2F0ID0gMC40OTsgICAgICAgICUgW1RdIC0gc2F0dXJhdGlvbiBmbHV4" +"IGRlbnNpdHkKCiUlIENlbnRlciBsZWcKQV9jZW50ZXIgPSAxNzhlLTY7ICAgJSBbbV4yXSAtIGNl" +"bnRlciBsZWcgY3Jvc3Mtc2VjdGlvbgpsX2NlbnRlciA9IDQyZS0zOyAgICAlIFttXSAtIGNlbnRl" +"ciBsZWcgbGVuZ3RoCgolJSBPdXRlciBsZWdzCkFfb3V0ZXIgPSA4OWUtNjsgICAgICUgW21eMl0g" +"LSBvdXRlciBsZWcgY3Jvc3Mtc2VjdGlvbgpsX291dGVyID0gNDJlLTM7ICAgICAlIFttXSAtIG91" +"dGVyIGxlZyBsZW5ndGgKCiUlIFRvcC9ib3R0b20gcGxhdGVzCkFfcGxhdGUgPSA4OWUtNjsgICAg" +"ICUgW21eMl0gLSBwbGF0ZSBjcm9zcy1zZWN0aW9uCmxfcGxhdGUgPSAyNy42ZS0zOyAgICUgW21d" +"IC0gcGxhdGUgbGVuZ3RoIChlYWNoIHNpZGUpCgolJSBBaXIgZ2FwIChjZW50ZXIgbGVnKQpsX2dh" +"cCA9IDEuMGUtMzsgICAgICAlIFttXSAtIGFpciBnYXAgbGVuZ3RoCkFfZ2FwID0gMTc4ZS02OyAg" +"ICAgICUgW21eMl0gLSBhaXIgZ2FwIGNyb3NzLXNlY3Rpb24KCiUlIERlcml2ZWQ6IG91dGVyIGxl" +"ZyB0b3RhbCBsZW5ndGggaW5jbHVkZXMgcGxhdGVzCmxfb3V0ZXJfdG90YWwgPSBsX291dGVyICsg" +"MipsX3BsYXRlOyAgJSBbbV0gLSB0b3RhbCBvdXRlciBwYXRoIGxlbmd0aAo=" + InitialState "1" + SystemState "" + TaskingMode "1" + TaskConfigurations "" + CodeGenParameterInlining "2" + CodeGenFloatingPointFormat "2" + CodeGenAbsTimeUsageMsg "3" + CodeGenBaseName "" + CodeGenOutputDir "" + CodeGenExtraOpts "" + CodeGenTarget "Generic" + CodeGenTargetSettings "" + ExtendedMatrixPrecision "1" + MatrixSignificanceCheck "2" + EnableStateSpaceSplitting "2" + DisplayStateSpaceSplitting "1" + DiscretizationMethod "2" + ExternalModeSettings "" + AlgebraicLoopMethod "1" + AlgebraicLoopTolerance "1e-6" + ScriptsDialogGeometry "" + ScriptsDialogSplitterPos "0" + Schematic { + Location [431, 179; 1266, 954] + ZoomFactor 1 + SliderPosition [0, 0] + ShowBrowser off + BrowserWidth 100 + Component { + Type MagneticInterface + Name "MagIntPri" + Show off + Position [440, 390] + Direction up + Flipped off + LabelPosition east + Parameter { + Variable "n" + Value "n_prim" + Show off + } + Parameter { + Variable "Polarity" + Value "1" + Show off + } + } + Component { + Type MagneticInterface + Name "MagIntSec" + Show off + Position [440, 330] + Direction down + Flipped on + LabelPosition west + Parameter { + Variable "n" + Value "n_sndry" + Show off + } + Parameter { + Variable "Polarity" + Value "1" + Show off + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_sat" + Name "P_satCenter" + Show off + Position [440, 470] + Direction up + Flipped off + LabelPosition east + Frame [-8, -15; 8, 15] + Parameter { + Variable "fitting" + Value "1" + Show off + } + Parameter { + Variable "A" + Value "A_center" + Show off + } + Parameter { + Variable "l" + Value "l_center" + Show off + } + Parameter { + Variable "mu_r_unsat" + Value "mu_r" + Show off + } + Parameter { + Variable "mu_r_sat" + Value "1" + Show off + } + Parameter { + Variable "B_sat" + Value "B_sat" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -20] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 20] + Direction down + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_air" + Name "P_airGap" + Show off + Position [440, 270] + Direction up + Flipped on + LabelPosition east + Frame [-8, -10; 8, 10] + Parameter { + Variable "A" + Value "A_gap" + Show off + } + Parameter { + Variable "l" + Value "l_gap" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -15] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 15] + Direction down + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_sat" + Name "P_satOuterL" + Show off + Position [310, 380] + Direction up + Flipped off + LabelPosition west + Frame [-8, -15; 8, 15] + Parameter { + Variable "fitting" + Value "1" + Show off + } + Parameter { + Variable "A" + Value "A_outer" + Show off + } + Parameter { + Variable "l" + Value "l_outer_total" + Show off + } + Parameter { + Variable "mu_r_unsat" + Value "mu_r" + Show off + } + Parameter { + Variable "mu_r_sat" + Value "1" + Show off + } + Parameter { + Variable "B_sat" + Value "B_sat" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -20] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 20] + Direction down + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_sat" + Name "P_satOuterR" + Show off + Position [570, 380] + Direction up + Flipped off + LabelPosition east + Frame [-8, -15; 8, 15] + Parameter { + Variable "fitting" + Value "1" + Show off + } + Parameter { + Variable "A" + Value "A_outer" + Show off + } + Parameter { + Variable "l" + Value "l_outer_total" + Show off + } + Parameter { + Variable "mu_r_unsat" + Value "mu_r" + Show off + } + Parameter { + Variable "mu_r_sat" + Value "1" + Show off + } + Parameter { + Variable "B_sat" + Value "B_sat" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -20] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 20] + Direction down + } + } + Component { + Type ACVoltageSource + Name "V_ac" + Show on + Position [205, 390] + Direction down + Flipped on + LabelPosition east + Parameter { + Variable "V" + Value "V_p" + Show off + } + Parameter { + Variable "w" + Value "2*pi*f_sin" + Show off + } + Parameter { + Variable "phi" + Value "0" + Show off + } + } + Component { + Type Resistor + Name "R1" + Show on + Position [635, 330] + Direction up + Flipped off + LabelPosition east + Parameter { + Variable "R" + Value "RL" + Show off + } + } + Component { + Type Scope + Name "Scope" + Show on + Position [720, 200] + Direction up + Flipped off + LabelPosition south + Location [588, 348; 938, 595] + State "AAAA/wAAAAD9AAAAAgAAAAEAAAAAAAAAAPwCAAAAA/sAAAAQAFoAbwBvA" +"G0AQQByAGUAYQAAAAAA/////wAAADQA////+wAAABQAUwBhAHYAZQBkAFYAaQBlAHcAcwAAAAAA//" +"///wAAAGYA////+wAAAAwAVAByAGEAYwBlAHMAAAAAAP////8AAABmAP///wAAAAMAAAAAAAAAAPw" +"BAAAAAfsAAAAUAEQAYQB0AGEAVwBpAGQAZwBlAHQAAAAAAP////8AAABQAP///wAAAV4AAADcAAAA" +"BAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAEAAAAOAFQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAA" +"AAAAA==" + SavedViews "AAAAAgAAAAA=" + HeaderState "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAL/gMAAAAJAAAAB" +"AAAAGQAAAAFAAAAZAAAAAYAAABkAAAABwAAAGQAAAABAAAAZAAAAAIAAABkAAAAAwAAAGQAAAAIAA" +"AAZAAAAAkAAABkAAAAygAAAAsBAAABAAAAAAAAAAAAAAAAZP////8AAACBAAAAAAAAAAsAAACmAAA" +"AAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAA" +"AAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAA" +"AAkAAAAAQAAAAAAAAPoAAAAACQ=" + PlotPalettes "AAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + Axes "1" + TimeRange "0.0" + ScrollingMode "1" + SingleTimeAxis "1" + Open "0" + Ts "-1" + SampleLimit "0" + XAxisLabel "Time / s" + ShowLegend "1" + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + SignalTypes [ ] + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + Fourier { + SingleXAxis on + AxisLabel "Frequency / Hz" + Scaling 0 + PhaseDisplay 0 + ShowFourierLegend off + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + } + } + Component { + Type PlecsProbe + Name "Probe" + Show on + Position [660, 200] + Direction right + Flipped off + LabelPosition south + Probe { + Component "R1" + Path "" + Signals {"Resistor voltage"} + } + Probe { + Component "V_ac" + Path "" + Signals {"Source voltage"} + } + } + Connection { + Type Magnetic + SrcComponent "MagIntPri" + SrcTerminal 3 + DstComponent "MagIntSec" + DstTerminal 4 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 2 + DstComponent "MagIntSec" + DstTerminal 3 + } + Connection { + Type Magnetic + SrcComponent "P_satCenter" + SrcTerminal 1 + DstComponent "MagIntPri" + DstTerminal 4 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 1 + Points [440, 240; 310, 240] + DstComponent "P_satOuterL" + DstTerminal 1 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 1 + Points [440, 240; 570, 240] + DstComponent "P_satOuterR" + DstTerminal 1 + } + Connection { + Type Magnetic + SrcComponent "P_satCenter" + SrcTerminal 2 + Points [440, 510; 310, 510] + DstComponent "P_satOuterL" + DstTerminal 2 + } + Connection { + Type Magnetic + SrcComponent "P_satCenter" + SrcTerminal 2 + Points [440, 510; 570, 510] + DstComponent "P_satOuterR" + DstTerminal 2 + } + Connection { + Type Wire + SrcComponent "MagIntPri" + SrcTerminal 1 + Points [365, 375; 205, 375] + DstComponent "V_ac" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "MagIntPri" + SrcTerminal 2 + Points [365, 405; 205, 405] + DstComponent "V_ac" + DstTerminal 2 + } + Connection { + Type Wire + SrcComponent "MagIntSec" + SrcTerminal 1 + Points [600, 315; 635, 315] + DstComponent "R1" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "MagIntSec" + SrcTerminal 2 + Points [600, 345; 635, 345] + DstComponent "R1" + DstTerminal 2 + } + Connection { + Type Signal + SrcComponent "Probe" + SrcTerminal 1 + DstComponent "Scope" + DstTerminal 1 + } + } +} diff --git a/tests/testData/plecs/saturable_inductor.plecs b/tests/testData/plecs/saturable_inductor.plecs new file mode 100644 index 0000000..933b152 --- /dev/null +++ b/tests/testData/plecs/saturable_inductor.plecs @@ -0,0 +1,338 @@ +Plecs { + Name "saturable_inductor" + Version "4.7" + CircuitModel "ContStateSpace" + StartTime "0.0" + TimeSpan "tsim" + Timeout "" + Solver "dopri" + MaxStep "1" + InitStep "1" + FixedStep "1e-3" + Refine "1" + ZCStepSize "1e-9" + RelTol "1e-3" + AbsTol "-1" + TurnOnThreshold "0" + SyncFixedStepTasks "2" + UseSingleCommonBaseRate "2" + LossVariableLimitExceededMsg "3" + NegativeSwitchLossMsg "2" + DivisionByZeroMsg "2" + StiffnessDetectionMsg "2" + MaxConsecutiveZCs "10000" + AlgebraicLoopWithStateMachineMsg "2" + AssertionAction "1" + InitializationCommands base64 "JSUgRmlsZSAzOiBzYXR1cmFibGVfaW5kdWN0b3IucGxlY3MKJSUgVG9wb2xvZ3k6IFNpbmdsZS13" +"aW5kaW5nIGdhcHBlZCBpbmR1Y3RvciB3aXRoIG5vbmxpbmVhciBzYXR1cmFibGUgY29yZQolJSAK" +"JSUgUGFyYW1ldGVyCVZhbHVlCiUlIENvcmUgc2hhcGUJRTMyLzE2LzkKJSUgQ29yZSBtYXRlcmlh" +"bAlOODcgLSBTYXR1cmFibGUgQ29yZSB3aXRoIEJIIGN1cnZlCiUlIENvcmUgY3Jvc3Mtc2VjdGlv" +"bglBID0gODMuMiBtbV4yID0gODMuMmUtNiBtXjIKJSUgQ29yZSBtYWduZXRpYyBwYXRoIGxlbmd0" +"aAlsX2NvcmUgPSA3Mi43IG1tID0gNzIuN2UtMyBtCiUlIEFpciBnYXAgbGVuZ3RoCWxfZ2FwID0g" +"MC41IG1tID0gMC41ZS0zIG0KJSUgV2luZGluZwlOID0gMjAgdHVybnMKCiUlIFNpbXVsYXRpb24K" +"dHNpbSA9IDAuMTsgICAgICAgICAgJSBbc10gLSBzaW11bGF0aW9uIHRpbWUKCiUlIEVsZWN0cmlj" +"YWwKVl9wID0gMTsgICAgICAgICAgICAgJSBbVl0gLSBwZWFrIHNpbmUKZl9zaW4gPSAxMDBlMzsg" +"ICAgICAgJSBbSHpdIC0gZnJlcXVlbmN5CgolJSBXaW5kaW5nCm5fdHVybnMgPSAyMDsgICAgICAg" +"ICUgW10gLSBudW1iZXIgb2YgdHVybnMKCiUlIENvcmUgcGFyYW1ldGVycwpBX2UgPSA4My4yZS02" +"OyAgICAgICAlIFttXjJdIC0gZWZmZWN0aXZlIGNyb3NzLXNlY3Rpb25hbCBhcmVhCmxfZSA9IDcy" +"LjdlLTM7ICAgICAgICUgW21dIC0gZWZmZWN0aXZlIG1hZ25ldGljIHBhdGggbGVuZ3RoCm11X3Ig" +"PSAyMjAwOyAgICAgICAgICUgW10gLSBpbml0aWFsIHJlbGF0aXZlIHBlcm1lYWJpbGl0eSAodW5z" +"YXR1cmF0ZWQpCm11XzAgPSA0KnBpKjFlLTc7ICAgJSBbSC9tXSAtIHBlcm1lYWJpbGl0eSBvZiBm" +"cmVlIHNwYWNlCkJfc2F0ID0gMC40OTsgICAgICAgICUgW1RdIC0gc2F0dXJhdGlvbiBmbHV4IGRl" +"bnNpdHkKCiUlIEJIIGN1cnZlIGRhdGEgKE44NyBmZXJyaXRlLCA3IHBvaW50cykKJSUgSCBbQS9t" +"XSAgICBCIFtUXQolJSAgIDAgICAgICAgICAwCiUlICA1MCAgICAgICAgIDAuMjAKJSUgMTAwICAg" +"ICAgICAgMC4zNQolJSAyMDAgICAgICAgICAwLjQyCiUlIDUwMCAgICAgICAgIDAuNDcKJSUgMTAw" +"MCAgICAgICAgMC40OQolJSA1MDAwICAgICAgICAwLjQ5CgolJSBBaXIgZ2FwIHBhcmFtZXRlcnMK" +"bF9nYXAgPSAwLjVlLTM7ICAgICAgJSBbbV0gLSBhaXIgZ2FwIGxlbmd0aApBX2dhcCA9IDgzLjJl" +"LTY7ICAgICAlIFttXjJdIC0gYWlyIGdhcCBjcm9zcy1zZWN0aW9uCg==" + InitialState "1" + SystemState "" + TaskingMode "1" + TaskConfigurations "" + CodeGenParameterInlining "2" + CodeGenFloatingPointFormat "2" + CodeGenAbsTimeUsageMsg "3" + CodeGenBaseName "" + CodeGenOutputDir "" + CodeGenExtraOpts "" + CodeGenTarget "Generic" + CodeGenTargetSettings "" + ExtendedMatrixPrecision "1" + MatrixSignificanceCheck "2" + EnableStateSpaceSplitting "2" + DisplayStateSpaceSplitting "1" + DiscretizationMethod "2" + ExternalModeSettings "" + AlgebraicLoopMethod "1" + AlgebraicLoopTolerance "1e-6" + ScriptsDialogGeometry "" + ScriptsDialogSplitterPos "0" + Schematic { + Location [531, 279; 1266, 854] + ZoomFactor 1 + SliderPosition [0, 0] + ShowBrowser off + BrowserWidth 100 + Component { + Type MagneticInterface + Name "MagInt1" + Show off + Position [440, 290] + Direction up + Flipped off + LabelPosition east + Parameter { + Variable "n" + Value "n_turns" + Show off + } + Parameter { + Variable "Polarity" + Value "1" + Show off + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_sat" + Name "P_satCore" + Show off + Position [405, 240] + Direction up + Flipped off + LabelPosition east + Frame [-8, -15; 8, 15] + Parameter { + Variable "fitting" + Value "2" + Show off + } + Parameter { + Variable "A" + Value "A_e" + Show off + } + Parameter { + Variable "l" + Value "l_e" + Show off + } + Parameter { + Variable "mu_r_unsat" + Value "mu_r" + Show off + } + Parameter { + Variable "mu_r_sat" + Value "1" + Show off + } + Parameter { + Variable "B_sat" + Value "B_sat" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Parameter { + Variable "BH_data" + Value "[0 0; 50 0.20; 100 0.35; 200 0.42; 500 0.47; 1000 0.49; 5000 0.49]" + Show off + } + Terminal { + Type MagneticPort + Position [0, -20] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 20] + Direction down + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_air" + Name "P_airGap" + Show off + Position [440, 345] + Direction up + Flipped on + LabelPosition east + Frame [-8, -10; 8, 10] + Parameter { + Variable "A" + Value "A_gap" + Show off + } + Parameter { + Variable "l" + Value "l_gap" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -15] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 15] + Direction down + } + } + Component { + Type ACVoltageSource + Name "V_ac" + Show on + Position [305, 290] + Direction down + Flipped on + LabelPosition east + Parameter { + Variable "V" + Value "V_p" + Show off + } + Parameter { + Variable "w" + Value "2*pi*f_sin" + Show off + } + Parameter { + Variable "phi" + Value "0" + Show off + } + } + Component { + Type Scope + Name "Scope" + Show on + Position [605, 170] + Direction up + Flipped off + LabelPosition south + Location [588, 348; 938, 595] + State "AAAA/wAAAAD9AAAAAgAAAAEAAAAAAAAAAPwCAAAAA/sAAAAQAFoAbwBvA" +"G0AQQByAGUAYQAAAAAA/////wAAADQA////+wAAABQAUwBhAHYAZQBkAFYAaQBlAHcAcwAAAAAA//" +"///wAAAGYA////+wAAAAwAVAByAGEAYwBlAHMAAAAAAP////8AAABmAP///wAAAAMAAAAAAAAAAPw" +"BAAAAAfsAAAAUAEQAYQB0AGEAVwBpAGQAZwBlAHQAAAAAAP////8AAABQAP///wAAAV4AAADcAAAA" +"BAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAEAAAAOAFQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAA" +"AAAAA==" + SavedViews "AAAAAgAAAAA=" + HeaderState "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAL/gMAAAAJAAAAB" +"AAAAGQAAAAFAAAAZAAAAAYAAABkAAAABwAAAGQAAAABAAAAZAAAAAIAAABkAAAAAwAAAGQAAAAIAA" +"AAZAAAAAkAAABkAAAAygAAAAsBAAABAAAAAAAAAAAAAAAAZP////8AAACBAAAAAAAAAAsAAACmAAA" +"AAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAA" +"AAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAA" +"AAkAAAAAQAAAAAAAAPoAAAAACQ=" + PlotPalettes "AAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + Axes "1" + TimeRange "0.0" + ScrollingMode "1" + SingleTimeAxis "1" + Open "0" + Ts "-1" + SampleLimit "0" + XAxisLabel "Time / s" + ShowLegend "1" + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + SignalTypes [ ] + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + Fourier { + SingleXAxis on + AxisLabel "Frequency / Hz" + Scaling 0 + PhaseDisplay 0 + ShowFourierLegend off + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + } + } + Component { + Type PlecsProbe + Name "Probe" + Show on + Position [545, 170] + Direction right + Flipped off + LabelPosition south + Probe { + Component "V_ac" + Path "" + Signals {"Source voltage"} + } + } + Connection { + Type Magnetic + SrcComponent "P_satCore" + SrcTerminal 1 + Points [405, 210; 440, 210] + DstComponent "MagInt1" + DstTerminal 3 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 2 + DstComponent "MagInt1" + DstTerminal 4 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 1 + Points [440, 370; 405, 370] + DstComponent "P_satCore" + DstTerminal 2 + } + Connection { + Type Wire + SrcComponent "MagInt1" + SrcTerminal 1 + Points [365, 275; 305, 275] + DstComponent "V_ac" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "MagInt1" + SrcTerminal 2 + Points [365, 305; 305, 305] + DstComponent "V_ac" + DstTerminal 2 + } + Connection { + Type Signal + SrcComponent "Probe" + SrcTerminal 1 + DstComponent "Scope" + DstTerminal 1 + } + } +} diff --git a/tests/testData/plecs/simple_inductor.plecs b/tests/testData/plecs/simple_inductor.plecs new file mode 100644 index 0000000..f4fbeb2 --- /dev/null +++ b/tests/testData/plecs/simple_inductor.plecs @@ -0,0 +1,346 @@ +Plecs { + Name "simple_inductor" + Version "4.7" + CircuitModel "ContStateSpace" + StartTime "0.0" + TimeSpan "tsim" + Timeout "" + Solver "dopri" + MaxStep "1" + InitStep "1" + FixedStep "1e-3" + Refine "1" + ZCStepSize "1e-9" + RelTol "1e-3" + AbsTol "-1" + TurnOnThreshold "0" + SyncFixedStepTasks "2" + UseSingleCommonBaseRate "2" + LossVariableLimitExceededMsg "3" + NegativeSwitchLossMsg "2" + DivisionByZeroMsg "2" + StiffnessDetectionMsg "2" + MaxConsecutiveZCs "10000" + AlgebraicLoopWithStateMachineMsg "2" + AssertionAction "1" + InitializationCommands "%% File 1: simple_inductor.plecs\n%% Topology: Singl" +"e-winding gapped inductor\n%% \n%% Parameter\tValue\n%% Core shape\tE32/16/9 " +"(simplified as single magnetic path)\n%% Core material\tN87, mu_r = 2200\n%% " +"Core cross-section\tA = 83.2 mm^2 = 83.2e-6 m^2\n%% Core magnetic path length" +"\tl_core = 72.7 mm = 72.7e-3 m\n%% Air gap length\tl_gap = 1.0 mm = 1.0e-3 m" +"\n%% Winding\tN = 20 turns\n\n%% Simulation\ntsim = 0.1; % [s] - sim" +"ulation time\n\n%% Electrical\nV_p = 1; % [V] - peak sine\nf_sin " +"= 100e3; % [Hz] - frequency\n\n%% Winding\nn_turns = 20; % [] - " +"number of turns\n\n%% Core parameters\nA_e = 83.2e-6; % [m^2] - effecti" +"ve cross-sectional area\nl_e = 72.7e-3; % [m] - effective magnetic path" +" length\nmu_r = 2200; % [] - relative permeability\nmu_0 = 4*pi*1e-7;" +" % [H/m] - permeability of free space\nB_sat = 0.49; % [T] - saturat" +"ion flux density\n\n%% Air gap parameters\nl_gap = 1.0e-3; % [m] - air g" +"ap length\nA_gap = 83.2e-6; % [m^2] - air gap cross-section\n" + InitialState "1" + SystemState "" + TaskingMode "1" + TaskConfigurations "" + CodeGenParameterInlining "2" + CodeGenFloatingPointFormat "2" + CodeGenAbsTimeUsageMsg "3" + CodeGenBaseName "" + CodeGenOutputDir "" + CodeGenExtraOpts "" + CodeGenTarget "Generic" + CodeGenTargetSettings "" + ExtendedMatrixPrecision "1" + MatrixSignificanceCheck "2" + EnableStateSpaceSplitting "2" + DisplayStateSpaceSplitting "1" + DiscretizationMethod "2" + ExternalModeSettings "" + AlgebraicLoopMethod "1" + AlgebraicLoopTolerance "1e-6" + ScriptsDialogGeometry "" + ScriptsDialogSplitterPos "0" + Schematic { + Location [-257, -1409; 1021, -784] + ZoomFactor 1 + SliderPosition [0, 0] + ShowBrowser off + BrowserWidth 100 + Component { + Type MagneticInterface + Name "MagInt1" + Show off + Position [440, 290] + Direction up + Flipped off + LabelPosition east + Parameter { + Variable "n" + Value "n_turns" + Show off + } + Parameter { + Variable "Polarity" + Value "1" + Show off + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_sat" + Name "P_satCore" + Show off + Position [405, 240] + Direction up + Flipped off + LabelPosition east + Frame [-8, -15; 8, 15] + Parameter { + Variable "fitting" + Value "1" + Show off + } + Parameter { + Variable "A" + Value "A_e" + Show off + } + Parameter { + Variable "l" + Value "l_e" + Show off + } + Parameter { + Variable "mu_r_unsat" + Value "mu_r" + Show off + } + Parameter { + Variable "mu_r_sat" + Value "1" + Show off + } + Parameter { + Variable "B_sat" + Value "B_sat" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -20] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 20] + Direction down + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_air" + Name "P_airGap" + Show off + Position [440, 345] + Direction up + Flipped on + LabelPosition east + Frame [-8, -10; 8, 10] + Parameter { + Variable "A" + Value "A_gap" + Show off + } + Parameter { + Variable "l" + Value "l_gap" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -15] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 15] + Direction down + } + } + Component { + Type ACVoltageSource + Name "V_ac" + Show on + Position [275, 300] + Direction down + Flipped on + LabelPosition east + Parameter { + Variable "V" + Value "V_p" + Show off + } + Parameter { + Variable "w" + Value "2*pi*f_sin" + Show off + } + Parameter { + Variable "phi" + Value "0" + Show off + } + } + Component { + Type Scope + Name "Scope" + Show on + Position [605, 170] + Direction up + Flipped off + LabelPosition south + Location [588, 348; 938, 635] + State "AAAA/wAAAAD9AAAAAgAAAAEAAAAAAAAAAPwCAAAAA/sAAAAQAFoAbwBvA" +"G0AQQByAGUAYQAAAAAA/////wAAADQA////+wAAABQAUwBhAHYAZQBkAFYAaQBlAHcAcwAAAAAA//" +"///wAAAGYA////+wAAAAwAVAByAGEAYwBlAHMAAAAAAP////8AAABmAP///wAAAAMAAAAAAAAAAPw" +"BAAAAAfsAAAAUAEQAYQB0AGEAVwBpAGQAZwBlAHQAAAAAAP////8AAABQAP///wAAAV4AAAEEAAAA" +"BAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAEAAAAOAFQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAA" +"AAAAA==" + SavedViews "AAAAAgAAAAA=" + HeaderState "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAL/gMAAAAJAAAAB" +"QAAAGQAAAAEAAAAZAAAAAcAAABkAAAABgAAAGQAAAABAAAAZAAAAAMAAABkAAAAAgAAAGQAAAAJAA" +"AAZAAAAAgAAABkAAAAygAAAAsBAAABAAAAAAAAAAAAAAAAZP////8AAACBAAAAAAAAAAsAAACmAAA" +"AAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAA" +"AAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAA" +"AAkAAAAAQAAAAAAAAPoAAAAACQ=" + PlotPalettes "AAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + Axes "1" + TimeRange "0.0" + ScrollingMode "1" + SingleTimeAxis "1" + Open "1" + Ts "-1" + SampleLimit "0" + XAxisLabel "Time / s" + ShowLegend "1" + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + SignalTypes [ ] + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + Fourier { + SingleXAxis on + AxisLabel "Frequency / Hz" + Scaling 0 + PhaseDisplay 0 + ShowFourierLegend off + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + } + } + Component { + Type PlecsProbe + Name "Probe" + Show on + Position [545, 170] + Direction right + Flipped off + LabelPosition south + Probe { + Component "V_ac" + Path "" + Signals {"Source voltage", "Source current"} + } + } + Component { + Type Resistor + Name "R1" + Show on + Position [330, 275] + Direction right + Flipped off + LabelPosition south + Parameter { + Variable "R" + Value "10e-3" + Show off + } + } + Connection { + Type Magnetic + SrcComponent "P_satCore" + SrcTerminal 1 + Points [405, 210; 440, 210] + DstComponent "MagInt1" + DstTerminal 3 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 2 + DstComponent "MagInt1" + DstTerminal 4 + } + Connection { + Type Magnetic + SrcComponent "P_airGap" + SrcTerminal 1 + Points [440, 370; 405, 370] + DstComponent "P_satCore" + DstTerminal 2 + } + Connection { + Type Wire + SrcComponent "MagInt1" + SrcTerminal 2 + Points [355, 305; 355, 320] + DstComponent "V_ac" + DstTerminal 2 + } + Connection { + Type Signal + SrcComponent "Probe" + SrcTerminal 1 + DstComponent "Scope" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "MagInt1" + SrcTerminal 1 + DstComponent "R1" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "R1" + SrcTerminal 2 + Points [275, 275] + DstComponent "V_ac" + DstTerminal 1 + } + } +} diff --git a/tests/testData/plecs/transformer.plecs b/tests/testData/plecs/transformer.plecs new file mode 100644 index 0000000..62b4a6c --- /dev/null +++ b/tests/testData/plecs/transformer.plecs @@ -0,0 +1,371 @@ +Plecs { + Name "transformer" + Version "4.7" + CircuitModel "ContStateSpace" + StartTime "0.0" + TimeSpan "tsim" + Timeout "" + Solver "dopri" + MaxStep "1" + InitStep "1" + FixedStep "1e-3" + Refine "1" + ZCStepSize "1e-9" + RelTol "1e-3" + AbsTol "-1" + TurnOnThreshold "0" + SyncFixedStepTasks "2" + UseSingleCommonBaseRate "2" + LossVariableLimitExceededMsg "3" + NegativeSwitchLossMsg "2" + DivisionByZeroMsg "2" + StiffnessDetectionMsg "2" + MaxConsecutiveZCs "10000" + AlgebraicLoopWithStateMachineMsg "2" + AssertionAction "1" + InitializationCommands base64 "JSUgRmlsZSAyOiB0cmFuc2Zvcm1lci5wbGVjcwolJSBUb3BvbG9neTogVHdvLXdpbmRpbmcgdHJh" +"bnNmb3JtZXIgd2l0aCBjb3JlIGxvc3Nlcywgbm8gYWlyIGdhcAolJSAKJSUgUGFyYW1ldGVyCVZh" +"bHVlCiUlIENvcmUgc2hhcGUJRTQyLzIxLzE1IChzaW1wbGlmaWVkIGFzIHNpbmdsZSBtYWduZXRp" +"YyBwYXRoKQolJSBDb3JlIG1hdGVyaWFsCU44NywgzrzhtaMgPSAyMjAwCiUlIENvcmUgY3Jvc3Mt" +"c2VjdGlvbglBID0gMTc4IG1twrIgPSAxNzhlLTYgbcKyCiUlIENvcmUgbWFnbmV0aWMgcGF0aCBs" +"ZW5ndGgJbF9jb3JlID0gOTcuMiBtbSA9IDk3LjJlLTMgbQolJSBBaXIgZ2FwCU5vbmUgKHVuZ2Fw" +"cGVkKQolJSBQcmltYXJ5IHdpbmRpbmcJTuKCgSA9IDI0IHR1cm5zCiUlIFNlY29uZGFyeSB3aW5k" +"aW5nCU7igoIgPSAxMiB0dXJucwolJSBDb3JlIGxvc3MgZXF1aXZhbGVudAlSbSA9IDUuMGU2IEHC" +"t3TCt3MvV2IKCgolJSBTaW11bGF0aW9uCnRzaW0gPSAwLjE7ICAgICAgICUgW3NdIC0gc2ltdWxh" +"dGlvbiB0aW1lCgolJSBFbGVjdHJpY2FsClZfcCA9IDE7ICAgICAgICAlIFtWXSAtIHBlYWsgc2lu" +"ZQpmX3NpbiA9IDEwMGUzCm5fcHJpbSA9IDI0OyAgICAgICAgJSBbXSAtIHByaW1hcnkgc2lkZSB0" +"dXJuIG51bWJlcgpuX3NuZHJ5ID0gMTI7ICAgICAgICUgW10gLSBzZWNvbmRhcnkgc2lkZSB0dXJu" +"IG51bWJlcgpSTD0xZTM7CgolJSBUcmFuc2Zvcm1lciBjb3JlIHBhcmFtZXRlcnMKQV9lID0gMTc4" +"ZS02OwkJICAlIFttXjJdIC0gZWZmZWN0aXZlIGNyb3NzLXNlY3Rpb25hbCBhcmVhIG9mIHRoZSB0" +"cmFuc2Zvcm1lciBjb3JlCmxfZSA9IDk3LjJlLTM7CQkgICUgW21dIC0gZWZmZWN0aXZlIG1hZ25l" +"dGljIHBhdGggbGVuZ3RoIG9mIHRoZSB0cmFuc2Zvcm1lciBjb3JlCm11X3IgPSAyMjAwOwkJICAl" +"IFtdIC0gcmVsYXRpdmUgcGVybWVhYmlsaXR5IG9mIHRoZSB0cmFuc2Zvcm1lciBjb3JlCm11XzAg" +"PSA0KnBpKjFlLTc7CSAgJSBbSC9tXSAtIHBlcm1lYWJpbGl0eSBvZiBmcmVlIHNwYWNlCkJfc2F0" +"ID0gMC40OTsJCSAgJSBbVF0gLSBzYXR1cmF0aW9uIGZsdXggZGVuc2l0eSBvZiB0aGUgdHJhbnNm" +"b3JtZXIgY29yZQpSbSA9IDUuMGU2OwkJICAlIFtBwrd0wrdzL1diXSAtIGNvcmUgbG9zcyBlcXVp" +"dmFsZW50IG1hZ25ldGljIHJlc2lzdGFuY2UK" + InitialState "1" + SystemState "" + TaskingMode "1" + TaskConfigurations "" + CodeGenParameterInlining "2" + CodeGenFloatingPointFormat "2" + CodeGenAbsTimeUsageMsg "3" + CodeGenBaseName "" + CodeGenOutputDir "" + CodeGenExtraOpts "" + CodeGenTarget "Generic" + CodeGenTargetSettings "" + ExtendedMatrixPrecision "1" + MatrixSignificanceCheck "2" + EnableStateSpaceSplitting "2" + DisplayStateSpaceSplitting "1" + DiscretizationMethod "2" + ExternalModeSettings "" + AlgebraicLoopMethod "1" + AlgebraicLoopTolerance "1e-6" + ScriptsDialogGeometry "" + ScriptsDialogSplitterPos "0" + Schematic { + Location [531, 279; 1266, 854] + ZoomFactor 1 + SliderPosition [0, 0] + ShowBrowser off + BrowserWidth 100 + Component { + Type MagneticInterface + Name "MagInt5" + Show off + Position [440, 320] + Direction up + Flipped off + LabelPosition east + Parameter { + Variable "n" + Value "n_prim" + Show off + } + Parameter { + Variable "Polarity" + Value "1" + Show off + } + } + Component { + Type MagneticInterface + Name "MagInt6" + Show off + Position [440, 260] + Direction down + Flipped on + LabelPosition west + Parameter { + Variable "n" + Value "n_sndry" + Show off + } + Parameter { + Variable "Polarity" + Value "1" + Show off + } + } + Component { + Type Reference + SrcComponent "Components/Magnetic/Components/P_sat" + Name "P_satTr" + Show off + Position [405, 240] + Direction up + Flipped off + LabelPosition east + Frame [-8, -15; 8, 15] + Parameter { + Variable "fitting" + Value "1" + Show off + } + Parameter { + Variable "A" + Value "A_e" + Show off + } + Parameter { + Variable "l" + Value "l_e" + Show off + } + Parameter { + Variable "mu_r_unsat" + Value "mu_r" + Show off + } + Parameter { + Variable "mu_r_sat" + Value "1" + Show off + } + Parameter { + Variable "B_sat" + Value "B_sat" + Show off + } + Parameter { + Variable "F_init" + Value "0" + Show off + } + Terminal { + Type MagneticPort + Position [0, -20] + Direction up + } + Terminal { + Type MagneticPort + Position [0, 20] + Direction down + } + } + Component { + Type MagneticResistance + Name "Rm_loss" + Show off + Position [440, 355] + Direction up + Flipped on + LabelPosition east + Parameter { + Variable "Rm" + Value "Rm" + Show off + } + } + Component { + Type ACVoltageSource + Name "V_ac" + Show on + Position [305, 320] + Direction down + Flipped on + LabelPosition east + Parameter { + Variable "V" + Value "V_p" + Show off + } + Parameter { + Variable "w" + Value "2*pi*f_sin" + Show off + } + Parameter { + Variable "phi" + Value "0" + Show off + } + } + Component { + Type Resistor + Name "R1" + Show on + Position [535, 260] + Direction up + Flipped off + LabelPosition east + Parameter { + Variable "R" + Value "RL" + Show off + } + } + Component { + Type Scope + Name "Scope" + Show on + Position [605, 170] + Direction up + Flipped off + LabelPosition south + Location [588, 348; 938, 595] + State "AAAA/wAAAAD9AAAAAgAAAAEAAAAAAAAAAPwCAAAAA/sAAAAQAFoAbwBvA" +"G0AQQByAGUAYQAAAAAA/////wAAADQA////+wAAABQAUwBhAHYAZQBkAFYAaQBlAHcAcwAAAAAA//" +"///wAAAGYA////+wAAAAwAVAByAGEAYwBlAHMAAAAAAP////8AAABmAP///wAAAAMAAAAAAAAAAPw" +"BAAAAAfsAAAAUAEQAYQB0AGEAVwBpAGQAZwBlAHQAAAAAAP////8AAABQAP///wAAAV4AAADcAAAA" +"BAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAEAAAAOAFQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAA" +"AAAAA==" + SavedViews "AAAAAgAAAAA=" + HeaderState "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAL/gMAAAAJAAAAB" +"AAAAGQAAAAFAAAAZAAAAAYAAABkAAAABwAAAGQAAAABAAAAZAAAAAIAAABkAAAAAwAAAGQAAAAIAA" +"AAZAAAAAkAAABkAAAAygAAAAsBAAABAAAAAAAAAAAAAAAAZP////8AAACBAAAAAAAAAAsAAACmAAA" +"AAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAA" +"AAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAA" +"AAkAAAAAQAAAAAAAAPoAAAAACQ=" + PlotPalettes "AAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + Axes "1" + TimeRange "0.0" + ScrollingMode "1" + SingleTimeAxis "1" + Open "0" + Ts "-1" + SampleLimit "0" + XAxisLabel "Time / s" + ShowLegend "1" + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + SignalTypes [ ] + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + Fourier { + SingleXAxis on + AxisLabel "Frequency / Hz" + Scaling 0 + PhaseDisplay 0 + ShowFourierLegend off + Axis { + Name "" + AutoScale 1 + MinValue 0 + MaxValue 1 + Signals {} + Untangle 0 + KeepBaseline off + BaselineValue 0 + } + } + } + Component { + Type PlecsProbe + Name "Probe" + Show on + Position [545, 170] + Direction right + Flipped off + LabelPosition south + Probe { + Component "R1" + Path "" + Signals {"Resistor voltage"} + } + Probe { + Component "V_ac" + Path "" + Signals {"Source voltage"} + } + } + Connection { + Type Magnetic + SrcComponent "MagInt5" + SrcTerminal 3 + DstComponent "MagInt6" + DstTerminal 4 + } + Connection { + Type Magnetic + SrcComponent "P_satTr" + SrcTerminal 1 + Points [405, 210; 440, 210] + DstComponent "MagInt6" + DstTerminal 3 + } + Connection { + Type Magnetic + SrcComponent "Rm_loss" + SrcTerminal 2 + DstComponent "MagInt5" + DstTerminal 4 + } + Connection { + Type Magnetic + SrcComponent "Rm_loss" + SrcTerminal 1 + Points [440, 375; 405, 375] + DstComponent "P_satTr" + DstTerminal 2 + } + Connection { + Type Wire + SrcComponent "MagInt5" + SrcTerminal 1 + Points [365, 305; 365, 295; 305, 295] + DstComponent "V_ac" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "MagInt5" + SrcTerminal 2 + Points [365, 335; 365, 345; 305, 345] + DstComponent "V_ac" + DstTerminal 2 + } + Connection { + Type Wire + SrcComponent "MagInt6" + SrcTerminal 1 + Points [505, 245; 505, 240] + DstComponent "R1" + DstTerminal 1 + } + Connection { + Type Wire + SrcComponent "MagInt6" + SrcTerminal 2 + Points [505, 275; 505, 280] + DstComponent "R1" + DstTerminal 2 + } + Connection { + Type Signal + SrcComponent "Probe" + SrcTerminal 1 + DstComponent "Scope" + DstTerminal 1 + } + } +}