diff --git a/include/bbp/sonata/config.h b/include/bbp/sonata/config.h index 62a22e67..93be23cb 100644 --- a/include/bbp/sonata/config.h +++ b/include/bbp/sonata/config.h @@ -418,6 +418,7 @@ class SONATA_API SimulationConfig linear, relative_linear, pulse, + sinusoidal, subthreshold, hyperpolarizing, synapse_replay, @@ -480,6 +481,17 @@ class SONATA_API SimulationConfig bool representsPhysicalElectrode = false; }; + struct InputSinusoidal: public InputBase { + /// The peak amplitude of the sinusoid. Given in nA. + double ampStart{}; + /// The frequency of the sinusoidal waveform. Given in Hz. + double frequency{}; + /// Timestep of generated signal in ms. Default is 0.025 ms + double dt{}; + /// Whether this input represents a physical electrode. Default is false + bool representsPhysicalElectrode = false; + }; + struct InputSubthreshold: public InputBase { /// A percentage adjusted from 100 of a cell's threshold current double percentLess{}; @@ -625,6 +637,7 @@ class SONATA_API SimulationConfig using Input = nonstd::variant(simConf, + "Sinusoidal") + .def_readonly("amp_start", + &SimulationConfig::InputSinusoidal::ampStart, + DOC_SIMULATIONCONFIG(InputSinusoidal, ampStart)) + .def_readonly("frequency", + &SimulationConfig::InputSinusoidal::frequency, + DOC_SIMULATIONCONFIG(InputSinusoidal, frequency)) + .def_readonly("dt", + &SimulationConfig::InputSinusoidal::dt, + DOC_SIMULATIONCONFIG(InputSinusoidal, dt)) + .def_readonly("represents_physical_electrode", + &SimulationConfig::InputSinusoidal::representsPhysicalElectrode, + DOC_SIMULATIONCONFIG(InputSinusoidal, representsPhysicalElectrode)); + py::class_(simConf, "Subthreshold") .def_readonly("percent_less", @@ -1039,6 +1054,7 @@ PYBIND11_MODULE(_libsonata, m) { .value("linear", SimulationConfig::InputBase::Module::linear) .value("relative_linear", SimulationConfig::InputBase::Module::relative_linear) .value("pulse", SimulationConfig::InputBase::Module::pulse) + .value("sinusoidal", SimulationConfig::InputBase::Module::sinusoidal) .value("subthreshold", SimulationConfig::InputBase::Module::subthreshold) .value("hyperpolarizing", SimulationConfig::InputBase::Module::hyperpolarizing) .value("synapse_replay", SimulationConfig::InputBase::Module::synapse_replay) diff --git a/python/generated/docstrings.h b/python/generated/docstrings.h index 51fb0182..33c81b61 100644 --- a/python/generated/docstrings.h +++ b/python/generated/docstrings.h @@ -833,6 +833,8 @@ static const char *__doc_bbp_sonata_SimulationConfig_InputBase_Module_ornstein_u static const char *__doc_bbp_sonata_SimulationConfig_InputBase_Module_pulse = R"doc()doc"; +static const char *__doc_bbp_sonata_SimulationConfig_InputBase_Module_sinusoidal = R"doc()doc"; + static const char *__doc_bbp_sonata_SimulationConfig_InputBase_Module_relative_linear = R"doc()doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputBase_Module_relative_ornstein_uhlenbeck = R"doc()doc"; @@ -911,6 +913,16 @@ static const char *__doc_bbp_sonata_SimulationConfig_InputPulse_representsPhysic static const char *__doc_bbp_sonata_SimulationConfig_InputPulse_width = R"doc(The length of time each pulse lasts (ms))doc"; +static const char *__doc_bbp_sonata_SimulationConfig_InputSinusoidal = R"doc()doc"; + +static const char *__doc_bbp_sonata_SimulationConfig_InputSinusoidal_ampStart = R"doc(The amount of current initially injected (nA))doc"; + +static const char *__doc_bbp_sonata_SimulationConfig_InputSinusoidal_frequency = R"doc(The frequency of the waveform (Hz))doc"; + +static const char *__doc_bbp_sonata_SimulationConfig_InputSinusoidal_dt = R"doc(Timestep of generated signal in ms. Default is 0.025 ms)doc"; + +static const char *__doc_bbp_sonata_SimulationConfig_InputSinusoidal_representsPhysicalElectrode = R"doc(Whether this input represents a physical electrode. Default is false)doc"; + static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeLinear = R"doc()doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeLinear_percentEnd = R"doc(The percentage of a cell's threshold current to inject at the end)doc"; diff --git a/python/tests/test_config.py b/python/tests/test_config.py index 31822814..b015a5e0 100644 --- a/python/tests/test_config.py +++ b/python/tests/test_config.py @@ -487,6 +487,8 @@ def test_basic(self): "ex_replay", "ex_seclamp", "ex_shotnoise", + "ex_sinusoidal", + "ex_sinusoidal_default_dt", "ex_subthreshold" }) @@ -514,6 +516,24 @@ def test_basic(self): self.assertEqual(self.config.input('ex_pulse').width, 1) self.assertEqual(self.config.input('ex_pulse').frequency, 80) + self.assertEqual(self.config.input('ex_sinusoidal').input_type.name, 'current_clamp') + self.assertEqual(self.config.input('ex_sinusoidal').module.name, 'sinusoidal') + self.assertEqual(self.config.input('ex_sinusoidal').delay, 10) + self.assertEqual(self.config.input('ex_sinusoidal').duration, 80) + self.assertEqual(self.config.input('ex_sinusoidal').node_set, "Mosaic") + self.assertEqual(self.config.input('ex_sinusoidal').frequency, 8) + self.assertEqual(self.config.input('ex_sinusoidal').amp_start, 0.2) + self.assertEqual(self.config.input('ex_sinusoidal').dt, 0.5) + + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').input_type.name, 'current_clamp') + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').module.name, 'sinusoidal') + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').delay, 10) + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').duration, 80) + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').node_set, "Mosaic") + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').frequency, 80) + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').amp_start, 2) + self.assertEqual(self.config.input('ex_sinusoidal_default_dt').dt, 0.025) + self.assertEqual(self.config.input('ex_noise_meanpercent').input_type.name, 'current_clamp') self.assertEqual(self.config.input('ex_noise_meanpercent').module.name, 'noise') self.assertEqual(self.config.input('ex_noise_meanpercent').delay, 0) diff --git a/src/config.cpp b/src/config.cpp index 1bed987d..5c98ad20 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -98,6 +98,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM( {SimulationConfig::InputBase::Module::linear, "linear"}, {SimulationConfig::InputBase::Module::relative_linear, "relative_linear"}, {SimulationConfig::InputBase::Module::pulse, "pulse"}, + {SimulationConfig::InputBase::Module::sinusoidal, "sinusoidal"}, {SimulationConfig::InputBase::Module::subthreshold, "subthreshold"}, {SimulationConfig::InputBase::Module::hyperpolarizing, "hyperpolarizing"}, {SimulationConfig::InputBase::Module::synapse_replay, "synapse_replay"}, @@ -438,6 +439,18 @@ SimulationConfig::Input parseInputModule(const nlohmann::json& valueIt, {false}); return ret; } + case Module::sinusoidal: { + SimulationConfig::InputSinusoidal ret; + parseCommon(ret); + parseMandatory(valueIt, "amp_start", debugStr, ret.ampStart); + parseMandatory(valueIt, "frequency", debugStr, ret.frequency); + parseOptional(valueIt, "dt", ret.dt, {0.025}); + parseOptional(valueIt, + "represents_physical_electrode", + ret.representsPhysicalElectrode, + {false}); + return ret; + } case Module::subthreshold: { SimulationConfig::InputSubthreshold ret; parseCommon(ret); @@ -1234,6 +1247,7 @@ class SimulationConfig::Parser if (!(nonstd::holds_alternative(input) || nonstd::holds_alternative(input) || nonstd::holds_alternative(input) || + nonstd::holds_alternative(input) || nonstd::holds_alternative(input) || nonstd::holds_alternative(input) || nonstd::holds_alternative(input) || diff --git a/tests/data/config/simulation_config.json b/tests/data/config/simulation_config.json index ecd2bf93..de2af14e 100644 --- a/tests/data/config/simulation_config.json +++ b/tests/data/config/simulation_config.json @@ -90,6 +90,25 @@ "duration": 80, "node_set": "Mosaic" }, + "ex_sinusoidal": { + "input_type": "current_clamp", + "module": "sinusoidal", + "frequency": 8, + "amp_start": 0.2, + "dt": 0.5, + "delay": 10, + "duration": 80, + "node_set": "Mosaic" + }, + "ex_sinusoidal_default_dt": { + "input_type": "current_clamp", + "module": "sinusoidal", + "frequency": 80, + "amp_start": 2, + "delay": 10, + "duration": 80, + "node_set": "Mosaic" + }, "ex_subthreshold": { "input_type": "current_clamp", "module": "subthreshold", diff --git a/tests/test_config.cpp b/tests/test_config.cpp index 2ef9e3b0..d4485b4f 100644 --- a/tests/test_config.cpp +++ b/tests/test_config.cpp @@ -431,6 +431,30 @@ TEST_CASE("SimulationConfig") { CHECK(input.ampStart == 2); CHECK(input.width == 1); } + { + const auto input = nonstd::get(config.getInput("ex_sinusoidal")); + CHECK(input.inputType == InputType::current_clamp); + CHECK(input.module == Module::sinusoidal); + CHECK(input.delay == 10); + CHECK(input.duration == 80); + CHECK(input.nodeSet == "Mosaic"); + + CHECK(input.frequency == 8); + CHECK(input.ampStart == 0.2); + CHECK(input.dt == 0.5); + } + { + const auto input = nonstd::get(config.getInput("ex_sinusoidal_default_dt")); + CHECK(input.inputType == InputType::current_clamp); + CHECK(input.module == Module::sinusoidal); + CHECK(input.delay == 10); + CHECK(input.duration == 80); + CHECK(input.nodeSet == "Mosaic"); + + CHECK(input.frequency == 80); + CHECK(input.ampStart == 2); + CHECK(input.dt == 0.025); + } { const auto input = nonstd::get(config.getInput("ex_subthreshold")); CHECK(input.inputType == InputType::current_clamp); @@ -567,6 +591,8 @@ TEST_CASE("SimulationConfig") { "ex_replay", "ex_seclamp", "ex_shotnoise", + "ex_sinusoidal", + "ex_sinusoidal_default_dt", "ex_subthreshold"}); auto overrides = config.getConnectionOverrides();