Commit 712e3ef5 authored by Elio Antonio Sacchetti's avatar Elio Antonio Sacchetti
Browse files

Modifications made according to 1st code review performed by @simonspa

parent e61018ee
Loading
Loading
Loading
Loading
+45 −101
Original line number Diff line number Diff line
@@ -40,8 +40,8 @@ NetlistWriterModule::NetlistWriterModule(Configuration& config, Messenger* messe
    config_.setDefault<std::string>("file_name", "output_netlist_event_");
    file_name_ = config.get<std::string>("file_name");

    // Default source type set to isource_pulse
    config_.setDefault<SourceType>("source_type", SourceType::isource_pulse);
    // Default source type set to ISOURCE_PULSE
    config_.setDefault<SourceType>("source_type", SourceType::ISOURCE_PULSE);
    source_type_ = config.get<SourceType>("source_type");

    // Get the source name and the circuit connected to it, as defined in the netlist
@@ -56,18 +56,7 @@ NetlistWriterModule::NetlistWriterModule(Configuration& config, Messenger* messe
    auto to_save = config_.getArray<std::string>("waveform_to_save");
    waveform_to_save_.insert(to_save.begin(), to_save.end());

    auto detector_model = detector_->getModel();
    std::string default_enumerator = "x * " + std::to_string(detector_model->getNPixels().Y()) + " + y";
    net_enumerator_ = std::make_unique<TFormula>("net_enumerator",
                                                 (config_.get<std::string>("net_enumerator", default_enumerator)).c_str());

    if(!net_enumerator_->IsValid()) {
        throw InvalidValueError(config_, "net_enumerator", "net enumerator is not a valid ROOT::TFormula expression.");
    }

    // Boolean to execute or not the external uelec simulation
    config_.setDefault<bool>("run_netlist_sim", false);
    run_netlist_simulation_ = config_.get<bool>("run_netlist_sim");

    // Options to add the the uelec simulation command
    if(config_.has("simulator_command")) {
@@ -75,8 +64,7 @@ NetlistWriterModule::NetlistWriterModule(Configuration& config, Messenger* messe
        simulator_command_ = config_.get<std::string>("simulator_command");
    }

    // Parameters for the isource_pulse option (pulse shape)
    // ?!?!?!? not sure of having correctly configured the units
    // Parameters for the ISOURCE_PULSE option (pulse shape)
    config_.setDefault<double>("t_delay", Units::get(0, "ns"));
    delay_ = config_.get<double>("t_delay");

@@ -88,22 +76,6 @@ NetlistWriterModule::NetlistWriterModule(Configuration& config, Messenger* messe

    config_.setDefault<double>("t_width", Units::get(3, "ns"));
    width_ = config_.get<double>("t_width");

    auto parameters = config_.getArray<double>("net_enumerator_parameters", {});

    // check if number of parameters match up
    if(static_cast<size_t>(net_enumerator_->GetNpar()) != parameters.size()) {
        throw InvalidValueError(
            config_,
            "net_enumerator_parameters",
            "The number of function parameters does not line up with the number of parameters in the function.");
    }

    for(size_t n = 0; n < parameters.size(); ++n) {
        net_enumerator_->SetParameter(static_cast<int>(n), parameters[n]);
    }

    LOG(DEBUG) << "Net enumerator function successfully initialized with " << parameters.size() << " parameters";
}

void NetlistWriterModule::initialize() {
@@ -117,42 +89,39 @@ void NetlistWriterModule::initialize() {
    LOG(DEBUG) << "Netlist file extension: " << extension;

    std::string line;
    std::ostringstream eof_netlist;
    std::ostringstream header_netlist;
    // regex for the isource line
    // regex for the ISOURCE line
    std::regex source_regex("\\((.+)\\)");
    // regex for the circuit line
    std::regex subckt_regex("^(\\w+)\\s+\\((.+)\\)\\s+(\\w+)");
    std::smatch connection_match;

    while(getline(netlist_file, line)) {
        // Writes the content of the netlist file to the new one
        // Identifies the isource declaration line
        // Identifies the isource instance nets names
        line_number++;
        // Writes the content of the netlist file to the new one
        file_lines.push_back(line);

        // For the isource nets
        // Identifies the ISOURCE declaration line
        if(line.rfind(source_name_, 0) == 0) {
            source_line_number = line_number;
            source_line_number_ = line_number;
            if(std::regex_search(line, connection_match, source_regex)) {
                LOG(INFO) << "Found connections in netlist template: " << connection_match[0];
                // connections_[1] instead of connections_[0], to get back the nets without the ()
                connections_ = connection_match[1];

                // the three following lines allow to have the two net names in separate variables
                // Identifies the ISOURCE instance nets names, the three following lines allow to have the two net names in
                // separate variables
                size_t space_pos = connections_.find(' ');
                source_net1_ = connections_.substr(0, space_pos);
                source_net2_ = connections_.substr(space_pos + 1);
            } else {
                throw ModuleError("Could not find net connections");
            }
            LOG(INFO) << "Found the source line!";
            LOG(DEBUG) << "Found the source line!";
        }

        if(line.rfind(subckt_instance_name_, 0) == 0) {
            // For the subckt nets
            subckt_line_number = line_number;
            subckt_line_number_ = line_number;
            if(std::regex_search(line, connection_match, subckt_regex)) {
                // connections_[1] instead of connections_[0], to get back the nets without the ()
                connections_ = connection_match[1];
@@ -162,43 +131,43 @@ void NetlistWriterModule::initialize() {
                LOG(INFO) << "Subckt nets: " << net;
                // Reads each word separated by a space and adds it to net_list
                while(iss >> net) {
                    net_list.push_back(net);
                    net_list_.push_back(net);
                }
                subckt_name = connection_match[3];
                LOG(INFO) << "Subckt name: " << subckt_name;
                subckt_name_ = connection_match[3];
                LOG(INFO) << "Subckt name: " << subckt_name_;
            } else {
                throw ModuleError("Could not find net connections of the subckt");
            }
            LOG(INFO) << "Found the subckt line!";
            LOG(DEBUG) << "Found the subckt line!";
        }
    }

    LOG(DEBUG) << "End of initialize";
}

void NetlistWriterModule::run(Event* event) {
    LOG(DEBUG) << "Module entered the run loop";

    // Messages: Fetch the (previously registered) messages for this event from the messenger:
    auto message = messenger_->fetchMessage<PixelChargeMessage>(this, event);

    if(message->getData().empty()) {
        LOG(INFO) << "No pixels fired, skipping even";
        LOG(DEBUG) << "No pixels fired, skipping even";
        return;
    }

    // Prepare output file for this event:
    const auto file_name = createOutputFile(file_name_ + "_event_" + std::to_string(event->number) + extension);
    const auto file_name = createOutputFile(file_name_ + "_event" + std::to_string(event->number) + extension);

    auto file = std::ofstream(file_name);
    LOG(INFO) << "Output file(s) created";

    // Write the header on the new netlist
    size_t current_line = 0;
    for(; current_line < std::min(source_line_number, subckt_line_number) - 1; current_line++) {
    for(; current_line < std::min(source_line_number_, subckt_line_number_) - 1; current_line++) {
        file << file_lines[current_line] << '\n';
    }

    // waveform to be saved
    std::ostringstream to_be_saved;

    // For loop over all pixels fired
    for(const auto& pixel_charge : message->getData()) {
        const auto& pixel = pixel_charge.getPixel();
@@ -210,7 +179,8 @@ void NetlistWriterModule::run(Event* event) {
            LOG(DEBUG) << "Received pixel " << pixel_index << ", charge " << Units::display(inputcharge, "e");

            // Get pixel address
            const auto idx = net_enumerator_->Eval(pixel_index.x(), pixel_index.y());
            auto detector_model = detector_->getModel();
            std::string idx = std::to_string(pixel_index.x() * static_cast<int>(detector_model->getNPixels().Y()) + pixel_index.y());

            if(target_ == Target::SPECTRE) {
                file << source_name_ << "\\<" << idx << "\\> (";
@@ -224,10 +194,7 @@ void NetlistWriterModule::run(Event* event) {
                    file << source_net1_ << "\\<" << idx << "\\> " << source_net2_ << "\\<" << idx << "\\>";
                }
            } else if(target_ == Target::SPICE) {
                // Selection of the first character of the source instance
                if((source_type_ == SourceType::isource) || (source_type_ == SourceType::isource_pulse)) {
                file << "I_" << idx << " ";
                }

                //   The source "gnd" (written "0") doesn't need to be incremented, this double "if" checks which net of the
                //   source the gnd is connected to Also a condition if none of the 2 nets are gnd
@@ -240,39 +207,39 @@ void NetlistWriterModule::run(Event* event) {
                }
            }

            // ------- isource -------
            // ------- ISOURCE-------

            if(source_type_ == SourceType::isource) {
            if(source_type_ == SourceType::ISOURCE) {

                // Get pulse and timepoints
                const auto& pulse = pixel_charge.getPulse(); // the pulse containing charges and times
                if(!pulse.isInitialized()) {
                    throw ModuleError("No pulse information available.");
                }
                cont auto step = pulse.getBinning();
                const auto step = pulse.getBinning();

                (target_ == Target::SPECTRE) ? file << ") isource delay=" << delay_ << "n type=pwl wave=[" : file << "PWL(";

                for(auto bin = pulse.begin(); bin != pulse.end(); ++bin) {
                    auto time = step * static_cast<double>(std::distance(pulse.begin(), bin)) * 1e-9;
                    double current_bin = *bin / step;
                    double current = current_bin * nanoCoulomb;
                    double current = current_bin * nanoCoulomb_;

                    file << std::setprecision(15) << time << " " << current << (bin < pulse.end() - 1 ? " " : "");
                }

                (target_ == Target::SPECTRE) ? file << "]\n" : file << ")\n";

                // ------- isource_pulse -------
                // ------- ISOURCE_PULSE -------

            } else if(source_type_ == SourceType::isource_pulse) {
            } else if(source_type_ == SourceType::ISOURCE_PULSE) {

                i_diode = (inputcharge * nanoCoulomb) / (rise_ / 2 + width_ + fall_ / 2);
                i_diode_ = (inputcharge * nanoCoulomb_) / (rise_ / 2 + width_ + fall_ / 2);

                (target_ == Target::SPECTRE)
                    ? (file << ") isource type=pulse val0=0 val1=" << i_diode << " delay=" << delay_ << "n rise=" << rise_
                    ? (file << ") isource type=pulse val0=0 val1=" << i_diode_ << " delay=" << delay_ << "n rise=" << rise_
                            << "n fall=" << fall_ << "n width=" << width_ << "n\n")
                    : (file << "PULSE(0 " << i_diode << " " << delay_ << "n " << rise_ << "n " << fall_ << "n " << width_
                    : (file << "PULSE(0 " << i_diode_ << " " << delay_ << "n " << rise_ << "n " << fall_ << "n " << width_
                            << "n)\n");
            }

@@ -281,7 +248,7 @@ void NetlistWriterModule::run(Event* event) {
                                        : (file << subckt_instance_name_ << "_" << idx << " ");

            // Select whether the net needs to be iterated or not
            for(const auto& net : net_list) {
            for(const auto& net : net_list_) {
                if(common_nets_.find(net) != common_nets_.end()) {
                    // must NOT be iterated !
                    file << net << " ";
@@ -294,7 +261,7 @@ void NetlistWriterModule::run(Event* event) {
            }
            file.seekp(-1, std::ios_base::cur);

            (target_ == Target::SPECTRE) ? file << ") " << subckt_name << "\n" : file << " " << subckt_name << "\n";
            (target_ == Target::SPECTRE) ? file << ") " << subckt_name_ << "\n" : file << " " << subckt_name_ << "\n";

            // Add the increment (address of fired pixel) on each waveform to save, and concatenate a single string
            // (added later to the generated netlist)
@@ -305,7 +272,7 @@ void NetlistWriterModule::run(Event* event) {
        }
    }

    for(current_line++; current_line < std::max(source_line_number, subckt_line_number) - 1; current_line++) {
    for(current_line++; current_line < std::max(source_line_number_, subckt_line_number_) - 1; current_line++) {
        file << file_lines[current_line] << '\n';
    }

@@ -317,28 +284,12 @@ void NetlistWriterModule::run(Event* event) {
    (target_ == Target::SPECTRE) ? file << "save " << to_be_saved.str() << '\n'
                                : file << ".save " << to_be_saved.str() << '\n';

    to_be_saved.str("");
    to_be_saved.clear();

    file.close();
    LOG(DEBUG) << "Successfully written netlist to file " << file_name;

    // Runs the external uelec simulation, if selected in the configuration file, on the same terminal (ie. with uelec soft.
    // env variables loaded)
    if(run_netlist_simulation_ == true) {

        if(target_ == Target::SPECTRE) {

            std::string uelec_sim_command = simulator_command_;
            uelec_sim_command += " ";
            uelec_sim_command += file_name;

            LOG(INFO) << uelec_sim_command;

            std::system(uelec_sim_command.c_str()); // NOLINT

        } else if(target_ == Target::SPICE) {

        std::string uelec_sim_command = simulator_command_;
        uelec_sim_command += " ";
        uelec_sim_command += file_name;
@@ -348,10 +299,3 @@ void NetlistWriterModule::run(Event* event) {
        std::system(uelec_sim_command.c_str()); // NOLINT
    }
}
}

void NetlistWriterModule::finalize() {
    // Possibly perform finalization of the module - if not, this method does not need to be implemented and can be
    // removed!
    LOG(INFO) << "Successfully finalized!";
}
+10 −16
Original line number Diff line number Diff line
@@ -41,8 +41,8 @@ namespace allpix {
        };

        enum class SourceType {
            isource,
            isource_pulse,
            ISOURCE,
            ISOURCE_PULSE,
        };

        /**
@@ -64,11 +64,6 @@ namespace allpix {
         */
        void run(Event* event) override;

        /**
         * @brief Finalization method of the module
         */
        void finalize() override;

    private:
        // Pointers to the central geometry manager and the messenger for interaction with the framework core:
        std::shared_ptr<Detector> detector_;
@@ -89,8 +84,6 @@ namespace allpix {
        std::set<std::string> common_nets_;

        std::set<std::string> waveform_to_save_;
        // waveform to be saved
        std::ostringstream to_be_saved;

        bool run_netlist_simulation_{};
        std::string simulator_command_{};
@@ -104,15 +97,16 @@ namespace allpix {
        std::string source_net1_;
        std::string source_net2_;

        std::vector<std::string> net_list;
        std::vector<std::string> net_list_;

        std::string source_line;
        std::string subckt_name;
        size_t subckt_line_number = 0;
        size_t source_line_number = 0;
        double nanoCoulomb = 1.6e-10;
        double i_diode = 0;
        std::string source_line_;
        std::string subckt_name_;
        size_t subckt_line_number_ = 0;
        size_t source_line_number_ = 0;
        double nanoCoulomb_ = 1.6e-10;
        double i_diode_ = 0;

        std::vector<std::string> file_lines;
    };
} // namespace allpix
 
 No newline at end of file
+15 −23
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ The netlist template needs to be formatted as described and illustrated (`SPECTR
* The sub-circuit written as an instance, connected to the source.
* The netlist footer and the simulator options.

```shell
```ini
--- netlist header ---

subckt front_end Pix_in Comp_vout Comp_vref SUB VDDA VSSA Vfbk
@@ -45,28 +45,23 @@ One way to get a netlist already formatted could be to extract it from the Caden

A new netlist is written for each event, reusing the header, footer, and circuit description from the netlist template specified with the `netlist_template` parameter. For each fired pixel, a source / circuit instance pair is added to the template.

The new source written can be parameterized with the parameter `source_type`. Two different types of sources can be used: `isource` and `isource_pulse`:
The new source written can be parameterized with the parameter `source_type`. Two different types of sources can be used: `ISOURCE` and `ISOURCE_PULSE`:

* `isource` allows writing all the temporal current waveform using a PWL (Piecewise Linear). This requires the use of the `[PulseTransfer]` module to get the current waveform. A delay can also be added using `t_delay`
* In order to lightweight the generated netlists, the `isource_pulse` can be selected: it uses the total collected charge Q (instead of the current pulse). Charge and current are linked by $ Q = \int I(t)dt $. The current pulse is set with the parameters `t_delay`, `t_rise`, `t_width` and `t_fall`. The following equation is then used to determine the current: $ I=\frac{Q}{\frac{t_{rise}+t_{fall}}{2}+t_{width}} $

<!--
The other possible source type is the `vsource_pulse`: using the total collected charge Q and the collecting electrode capacitance C, the voltage $U={Q}/{C}$ is written in the netlist.
* `ISOURCE` allows writing all the temporal current waveform using a PWL (Piecewise Linear). This requires the use of the `[PulseTransfer]` module to get the current waveform. A delay can also be added using `t_delay`
* In order to lightweight the generated netlists, the `ISOURCE_PULSE` can be selected: it uses the total collected charge Q (instead of the current pulse). Charge and current are linked by $ Q = \int I(t)dt $. The current pulse is set with the parameters `t_delay`, `t_rise`, `t_width` and `t_fall`. The following equation is then used to determine the current: $ I=\frac{Q}{\frac{t_{rise}+t_{fall}}{2}+t_{width}} $

The generated netlist file name can be configured with a prefix taken from the `file_name` parameter, and contains the number of the event the netlist was generated for.

The pixel address is used to identify the fired pixels in the netlist. If we consider the pixel (6,3) fired (7th column and 4th row) in a 10x10 matrix, the index 63 will be written in the netlist for this pixel (linearization of the 2D x and y indexes).

The parameter `waveform_to_save` is used to write at the end of the generated netlist the waveform(s) to be saved (always using the index notation to identify the fired pixels).

The electrical circuit simulation can be performed within the Allpix Squared event using the boolean parameter `run_netlist_sim` (default to `False`). If performed, the electrical simulation puts in stand-by the execution of the event.
The simulator command to execute must be given using the parameter `simulator_command`. The generated netlist name to execute is appended at the end of the command, as illustrated below for `SPECTRE` syntax:
The electrical circuit simulation can be performed within the Allpix Squared event using the parameter `simulator_command` which is used to specify the command line to execut. Not setting it switches the feature off, setting a value will enable it. The generated netlist name to execute is appended at the end of the command, as illustrated below for `SPECTRE` syntax:

```shell
spectre -f nutascii <file_name_event1.scs>
```

This electrical simulation is performed in the same terminal as the Allpix event, thus requiring the electrical simulator environment to be correctly set.
If performed, the electrical simulation puts in stand-by the execution of the event. This electrical simulation is performed in the same terminal as the Allpix event, thus requiring the electrical simulator environment to be correctly set.


## Parameters
@@ -74,43 +69,41 @@ This electrical simulation is performed in the same terminal as the Allpix event
* `target`: Syntax for the additional data to be written in the netlist, either `SPECTRE` or `SPICE`.
* `netlist_template`: Location of file containing the netlist template of the circuit in one of the supported formats.
* `file_name` : Generated netlist prefix name (the suffix is the event number).
* `source_type`: Type of current source to be used, `isource` and `isource_pulse`.
* `source_type`: Type of current source to be used, `ISOURCE` and `ISOURCE_PULSE`.
* `source_name`: Name of the current source instance in the netlist.
* `subckt_name`: Name of the circuit the source is connected to.
* `common_nets`: Nets shared between the pixels.
* `t_delay`: delay from 0 before the current pulse starts, default to 0 ns
* `t_rise`: rise time of the current pulse, default to 1 ns, only works for the `isource_pulse`
* `t_width`: width of the current pulse, default to 3 ns, only works for the `isource_pulse`
* `t_fall`: fall time of the current pulse, default to 1 ns, only works for the `isource_pulse`
* `t_rise`: rise time of the current pulse, default to 1 ns, only works for the `ISOURCE_PULSE`
* `t_width`: width of the current pulse, default to 3 ns, only works for the `ISOURCE_PULSE`
* `t_fall`: fall time of the current pulse, default to 1 ns, only works for the `ISOURCE_PULSE`
* `waveform_to_save`: Name of the waveforms to save
* `run_netlist_sim`: Boolean flag to select whether running the circuit simulation or not, default to false. The simulator (either `SPECTRE` or `SPICE`) environment must be loaded to run the circuit simulation in the event.
* `simulator_command`: Command to be exuted in the terminal, the generated netlist name is appended at the end of the command
* `simulator_command`: If specified, launches the electrical simulation. Command to be exuted in the terminal, the generated netlist name is appended at the end of the command.


## Usage

A possible configuration is using a `isource` and the `SPICE` syntax, requiring the collecting electrode capacitance:
A possible configuration is using a `ISOURCE` and the `SPICE` syntax, requiring the collecting electrode capacitance:

```ini
[NetlistWriter]
target = SPICE
netlist_template = "front_end.asc"
source_type = isource
source_type = ISOURCE
source_name = Instance_pulse
subckt_name = Instance_front_end
common_nets = Comp_vref, SUB, VDDA, VSSA, Vfbk
waveform_to_save = Pix_in, CSA_out, Comp_vout
run_netlist_sim = 1
simulator_command = "wine 'your\path\LTSpiceXVII\XVIIx64.exe' -run"
```

A current pulse `isource_pulse` and the `SPECTRE` syntax is used is this example:
A current pulse `ISOURCE_PULSE` and the `SPECTRE` syntax is used is this example:

```ini
[NetlistWriter]
target = SPECTRE
netlist_template = "front_end.scs"
source_type = isource_pulse
source_type = ISOURCE_PULSE
t_delay = 200ns
t_rise = 5ns
t_width = 20ns
@@ -119,6 +112,5 @@ source_name = Instance_pulse
subckt_name = Instance_front_end
common_nets = Comp_vref, SUB, VDDA, VSSA, Vfbk
waveform_to_save = Comp_vout
run_netlist_sim = 1
simulator_command = "spectre +aps -warn -info -log -debug -f nutascii"
```