/*
* Copyright (c) 2005–2012 Goethe Center for Scientific Computing - Simulation and Modelling (G-CSC Frankfurt)
* Copyright (c) 2012-2015 Goethe Center for Scientific Computing - Computational Neuroscience (G-CSC Frankfurt)
*
* This file is part of NeuGen.
*
* NeuGen is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* see: http://opensource.org/licenses/LGPL-3.0
* file://path/to/NeuGen/LICENSE
*
* NeuGen is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* This version of NeuGen includes copyright notice and attribution requirements.
* According to the LGPL this information must be displayed even if you modify
* the source code of NeuGen. The copyright statement/attribution may not be removed.
*
* Attribution Requirements:
*
* If you create derived work you must do the following regarding copyright
* notice and author attribution.
*
* Add an additional notice, stating that you modified NeuGen. In addition
* you must cite the publications listed below. A suitable notice might read
* "NeuGen source code modified by YourName 2012".
*
* Note, that these requirements are in full accordance with the LGPL v3
* (see 7. Additional Terms, b).
*
* Publications:
*
* S. Wolf, S. Grein, G. Queisser. NeuGen 2.0 -
* Employing NeuGen 2.0 to automatically generate realistic
* morphologies of hippocapal neurons and neural networks in 3D.
* Neuroinformatics, 2013, 11(2), pp. 137-148, doi: 10.1007/s12021-012-9170-1
*
*
* J. P. Eberhard, A. Wanner, G. Wittum. NeuGen -
* A tool for the generation of realistic morphology
* of cortical neurons and neural networks in 3D.
* Neurocomputing, 70(1-3), pp. 327-343, doi: 10.1016/j.neucom.2006.01.028
*
*/
package org.neugen.parsers;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import javax.vecmath.Point3f;
import org.apache.log4j.Logger;
import org.neugen.datastructures.Axon;
import org.neugen.datastructures.Cellipsoid;
import org.neugen.datastructures.Cons;
import org.neugen.datastructures.Dendrite;
import org.neugen.datastructures.Net;
import org.neugen.datastructures.neuron.Neuron;
import org.neugen.datastructures.Section;
import org.neugen.datastructures.Segment;
import org.neugen.gui.NeuGenConstants;
import org.neugen.gui.Trigger;
import org.neugen.utils.Utils;
/**
* @author Alexander Wanner
* @author Simone Eberhard
* @author Sergei Wolf
*/
public final class HocWriter {
private static final Logger logger = Logger.getLogger(HocWriter.class.getName());
private Net net;
private File file;
private String hocFileName;
/**
* The postfix of the name of the file for the NetCon events data.
*/
private String netConEventsFilePostfix;
/**
* The postfix of the name of the file for the voltage data.
*/
private String voltageFilePostfix;
private Trigger trigger;
/// precision for HOC output
private static final String FORMAT = "%.12f";
/**
* @brief format the string to our precision We need to refrain from
* exponential notation, since some tools, i. e. NEURON, seem not to
* handle exponential notations when importing certain morphologies
* @author stephanmg
* @param number
*/
private String format(Number number) {
return String.format(FORMAT, number);
}
public HocWriter(Net net, File file) {
this.net = net;
this.file = file;
hocFileName = file.getName();
String[] str = hocFileName.split("\\.");
hocFileName = str[0];
trigger = Trigger.getInstance();
}
public void setNetConEventsFilePostfix(String netConEventsFilePostfix) {
this.netConEventsFilePostfix = netConEventsFilePostfix;
}
public void setVoltageFilePostfix(String voltageFilePostfix) {
this.voltageFilePostfix = voltageFilePostfix;
}
/**
* Function to write data of segment into hoc file
*
* @param fw the stream for the hoc file
* @param start_end for 0 write data of start of segment, for 1 write
* data of end of segment.
* @param segment the current segment
* @throws IOException
*/
public final void writetohocSegment(Writer fw, int start_end, Segment segment) throws IOException {
String data = "\t pt3dadd(";
fw.write(data);
if (start_end == 0) {
Point3f sstart = segment.getStart();
fw.write(format(sstart.x) + ", " + format(sstart.y) + ", " + format(sstart.z) + ", ");
fw.write(segment.getStartRadius() * 2 + ")\n");
}
if (start_end == 1) {
Point3f send = segment.getEnd();
fw.write(format(send.x) + ", " + format(send.y) + ", " + format(send.z) + ", ");
fw.write(segment.getEndRadius() * 2 + ")\n");
}
}
/**
* Function to write the data of cellipsoid into a hoc file.
*
* @param fw the stream for the hoc file.
* @param neuron the neuron of net
* @throws IOException
*/
public final void writetohocSoma(Writer fw, Neuron neuron, String name) throws IOException {
Cellipsoid soma = neuron.getSoma();
Section cyl = soma.getCylindricRepresentant();
if (cyl == null) {
cyl = soma.cylindricRepresentant();
}
int nsegs = cyl.getSegments().size();
fw.write(name + " {\n");
fw.write("\t nseg = " + nsegs + "\n\t pt3dclear()\n");
for (Segment segment : cyl.getSegments()) {
writetohocSegment(fw, 0, segment);
}
Segment segment = cyl.getSegments().get(nsegs - 1);
writetohocSegment(fw, 1, segment);
fw.write("}\n");
fw.flush();
}
/**
* Function to write the data of axon into a hoc file.
*
* @param fw the stream for the hoc file.
* @param nn the number of neuron.
* @throws IOException
*/
public final int writetohocAxon(Writer fw, Neuron neuron, int nn) throws IOException {
int nsections = 0;
Axon axon = neuron.getAxon();
if (axon.getFirstSection() == null) {
return 0;
}
Section.Iterator secIterator = axon.getFirstSection().getIterator();
while (secIterator.hasNext()) {
Section section = secIterator.next();
int nsegs = section.getSegments().size();
String secName = section.getName();
String data = "create " + "N" + nn + secName + "\n";
fw.write(data);
Section parSec;
if (section.getParentalLink() != null) {
parSec = section.getParentalLink().getParental();
} else {
parSec = neuron.getSoma().getCylindricRepresentant();
}
float fractAlongParent = parSec.getFractAlongParentForChild(section);
//connect N0axon_hillock(0), N0soma(0)
if (nsections == 0) {
data = "connect " + "N" + nn + secName + "(0), " + "N" + nn + "soma" + "(" + (int) fractAlongParent + ")\n";
fw.write(data);
} //connect N0axon_myel_0000(0), N0axon_000(1)
else {
String parentSecName = parSec.getName();
data = "connect " + "N" + nn + secName + "(0), " + "N" + nn + parentSecName + "(" + (int) fractAlongParent + ")\n";
fw.write(data);
}
fw.write("N" + nn + secName + " {\n");
fw.write("\t nseg = " + nsegs + "\n\t pt3dclear()\n");
for (Segment segment : section.getSegments()) {
writetohocSegment(fw, 0, segment);
}
Segment segment = section.getSegments().get(nsegs - 1);
writetohocSegment(fw, 1, segment);
fw.write("}\n");
fw.flush();
++nsections;
}
return nsections;
}
/**
* Function to write the data of dendrite into a hoc file.
*
* @param fw the stream for the hoc file.
* @param neuron the neuron of neural network
* @param nn the number of neuron.
* @param suffix is for naming issues. A section is names "NXXsuffix"
* @throws IOException
*/
public final int writetohocDendrite(Writer fw, Neuron neuron, int nn) throws IOException {
int nsections = 0;
for (Dendrite dendrite : neuron.getDendrites()) {
Section firstSection = dendrite.getFirstSection();
Section.Iterator secIterator = firstSection.getIterator();
while (secIterator.hasNext()) {
Section section = secIterator.next();
String secName = section.getName();
int nsegs = section.getSegments().size();
// the number of the dendrite of neuron for the hoc file
//String secName = nn + suffix + i + "_" + secIterator.getSectionAdress();
String data = "create " + "N" + nn + secName + "\n";
fw.write(data);
Section parSec;
if (section.getParentalLink() != null) {
parSec = section.getParentalLink().getParental();
} else {
parSec = neuron.getSoma().getCylindricRepresentant();
}
float fractAlongParent = parSec.getFractAlongParentForChild(section);
String parentSecName = parSec.getName();
if (nsections == 0 || parentSecName.contains("soma")) {
data = "connect " + "N" + nn + secName + "(0), " + "N" + nn + "soma" + "(" + (int) fractAlongParent + ")\n";
//data = "connect N" + secName + "(0), N" + nn + "soma(" + (int)fractAlongParent +")\n";
fw.write(data);
} else {
data = "connect " + "N" + nn + secName + "(0), " + "N" + nn + parentSecName + "(" + (int) fractAlongParent + ")\n";
//data = "connect N" + secName + "(0), N" + nn + suffix + i + "_" + secIterator.getSectionAdress().parentAdress+ "(" + (int)fractAlongParent +")\n";
fw.write(data);
}
fw.write("N" + nn + secName + " {\n");
fw.write("\t nseg = " + nsegs + "\n\t pt3dclear()\n");
for (Segment segment : section.getSegments()) {
writetohocSegment(fw, 0, segment);
}
Segment segment = section.getSegments().get(nsegs - 1);
writetohocSegment(fw, 1, segment);
fw.write("}\n");
fw.flush();
++nsections;
}
}
return nsections;
}
public final void writetohocNeuron(Writer fw, int neuronNum) throws IOException {
int i = neuronNum;
Neuron neuron = net.getNeuronList().get(i);
int axon_part_num = 0, den_part_num = 0;
String name = "N" + i + "soma";
fw.write("create " + name + "\n");
if (i == 0) {
fw.write("access " + "N0soma" + "\n");
}
writetohocSoma(fw, neuron, name);
axon_part_num = writetohocAxon(fw, neuron, i);
den_part_num = writetohocDendrite(fw, neuron, i);
fw.write("N" + i + "ax = " + axon_part_num + "\n");
fw.write("N" + i + "dend = " + den_part_num + "\n");
fw.flush();
}
/**
* Function to write a hoc file for the data of net. NEURON will write a
* file with the voltage data of the somata.
*
* @param fw stream for the hoc file
*/
public final void writetohocNet(Writer fw) throws IOException {
List<Neuron> neuronList = net.getNeuronList();
int nneuron = neuronList.size();
for (int i = 0; i < nneuron; i++) {
// write neuron geometry
writetohocNeuron(fw, i);
}
// write exp2 synapses
net.getHocData().writetohocExp2Synapses(fw, null);
fw.append("/* Input synapses. */ \n");
// write alpha synapses
net.getHocData().writetohocAlphaSynapses(fw);
fw.close();
// write model to file
String sm_fname = hocFileName + "model.hoc";
trigger.outPrintln("\t" + sm_fname);
String modelFilePath = file.getParentFile().getPath() + NeuGenConstants.FILE_SEP + sm_fname;
fw = new FileWriter(new File(modelFilePath));
fw.append("celsius = 37.0 \n"
+ "wopen(\"" + hocFileName + "." + voltageFilePostfix + "\") \n"
+ "xopen(\"" + hocFileName + ".hoc\") \n"
+ "strdef fname \n\n"
+ "proc wtvoltage(){ \n"
+ "\tfprint(\"%g \",t) \n");
int j = 0;
for (int i = 0; i < nneuron; i += j) {
fw.append("\tfprint(\"");
for (j = 0; j < 20; j++) {
if ((i + j) > (nneuron - 1)) {
break;
}
fw.append("%g ");
}
fw.append("\\n\"");
for (j = 0; j < 20; j++) {
if ((i + j) > (nneuron - 1)) {
break;
}
fw.append(",N" + (i + j) + "soma.v(.5)");
}
fw.append(") \n");
}
fw.append("} \n");
fw.append("proc mkmovie() {" + "\n"
+ " print t" + "\n"
+ " wtvoltage()" + "\n"
+ " cvode.event(t+1, \"mkmovie()\")" + "\n"
+ "}" + "\n\n");
net.getHocData().writetohocModel(fw);
net.getHocData().writetohocChannels(fw);
fw.append("v_init = -70 \n"
+ "t = 0 \n"
+ "tstop = 100 \n"
+ "dt = 0.025 \n"
+ "steps_per_ms = 5 \n"
+ "realtime = 0 \n"
+ "run() \n"
+ "wopen() \n"
+ "quit() \n");
fw.flush();
fw.close();
}
/**
* Function to write a hoc file for the data of net. NEURON will write a
* file with the voltage data of the somata and a second file with the
* data of NetCon events.
*
* @param fname the name of the file.
* @throws IOException
*/
public final void writetohocNetVoltageAndEvents(Writer fw) throws IOException { //(simone)
List<Neuron> neuronList = net.getNeuronList();
int nneuron = neuronList.size();
for (int i = 0; i < nneuron; i++) {
// write neuron geometry
this.writetohocNeuron(fw, i);
}
// write synapses to file
String c_fname = this.hocFileName + ".syn_coords";
trigger.outPrintln("\t" + c_fname);
String synapseFilePath = file.getParentFile().getPath() + NeuGenConstants.FILE_SEP + c_fname;
Writer synFW = new FileWriter(new File(synapseFilePath));
// write exp2 synapses
net.getHocData().writetohocExp2Synapses(fw, synFW);
fw.append("/* Input synapses. */ \n");
// write alpha synapses
net.getHocData().writetohocAlphaSynapses(fw);
fw.close();
// write model to file
String sm_fname = hocFileName + "model.hoc";
trigger.outPrintln("\t" + sm_fname);
String modelFilePath = file.getParentFile().getPath() + NeuGenConstants.FILE_SEP + sm_fname;
fw = new FileWriter(new File(modelFilePath));
fw.append("celsius = 37.0\n"
+ "objref voltagesfile, nceventsfile\n\n" //(simone)
+ "voltagesfile = new File()\n"
+ "nceventsfile = new File()\n\n"
+ "voltagesfile.wopen(\"" + hocFileName + "." + voltageFilePostfix + "\")\n\n"
+ "nceventsfile.wopen(\"" + hocFileName + "." + netConEventsFilePostfix + "\")\n\n"
+ "xopen(\"" + hocFileName + ".hoc\")\n\n\n"
//netConEvents.out (simone)
+ "objref list, netConList, netConI, tvec, idvec\n\n"
+ "tvec = new Vector() //vector for storing the times of recorded events\n"
+ "idvec = new Vector() //vector for storing the ids of recorded NetCons\n\n"
+ "//list of all NetCons\n"
+ "netConList = new List()\n\n");
List<Cons> synapseList = net.getSynapseList();
for (int n = 0; n < synapseList.size(); ++n) {
Cons synapse = synapseList.get(n);
//if index < 0 -> nf_synapse
if (synapse.getNeuron1() == null) {
continue;
}
fw.append("netConList.append(Nc" + n + ")" + "\n");
}
//voltages
fw.append("proc wtvoltage(){" + "\n");
fw.append("\tvoltagesfile.printf(\"%g \",t)" + "\n");
int j = 0;
for (int i = 0; i < nneuron; i += j) {
fw.append("\tvoltagesfile.printf(\"");
for (j = 0; j < 20; j++) {
if ((i + j) > (nneuron - 1)) {
break;
}
fw.append("%g ");
}
fw.append("\\n\"");
for (j = 0; j < 20; j++) {
if ((i + j) > (nneuron - 1)) {
break;
}
fw.append(",N" + (i + j) + "soma.v(.5)");
}
fw.append(")\n");
}
fw.append("}\n\n");
fw.append("proc mkmovie() {\n"
+ " print t\n"
+ " for i=0, netConList.count()-1 {\n"
+ " netConI = netConList.object(i)\n"
+ " netConI.record(tvec, idvec)\n"
+ " }\n\n"
+ " wtvoltage()" + "\n"
+ " cvode.event(t+1, \"mkmovie()\")\n"
+ "}\n\n");
net.getHocData().writetohocModel(fw);
net.getHocData().writetohocChannels(fw);
fw.append("\n");
fw.append("v_init = -70" + "\n"
+ "t = 0" + "\n"
+ "tstop = 100" + "\n"
+ "dt = 0.025" + "\n"
+ "steps_per_ms = 5" + "\n"
+ "realtime = 0" + "\n"
+ "run()" + "\n"
+ "//write output file from recorded NetCon vectors" + "\n"
+ "//each line contains the timestep and all ids of NetCons active in that timestep" + "\n"
+ "for z=0, tstop{" + "\n"
+ " nceventsfile.printf(\"%d\" , z)" + "\n"
+ " for tv=0, tvec.size()-1 {" + "\n"
+ " if(z<=tvec.x[tv] && tvec.x[tv]<(z+1)){" + "\n"
+ " nceventsfile.printf(\" %d% \", idvec.x[tv])" + "\n"
+ " }" + "\n"
+ " }" + "\n"
+ " nceventsfile.printf(\"\\n\")" + "\n"
+ "}" + "\n\n"
+ "wopen()" + "\n"
+ "quit()");
fw.flush();
fw.close();
}
/**
* Function to write a hoc file for the data of net. NEURON will write a
* file with the data of NetCon events.
*
* @param fname the name of the file.
* @param events_postfix the postfix of the name of the file for the
* NetCon events data.
*/
public final void writetohocNetOnlyEvents(File file, String events_postfix, Writer fw) throws IOException { //(simone)
List<Neuron> neuronList = net.getNeuronList();
int nneuron = neuronList.size();
for (int i = 0; i < nneuron; i++) {
// write neuron geometry
this.writetohocNeuron(fw, i);
}
// write synapses to file
String c_fname = this.hocFileName + ".syn_coords";
trigger.outPrintln("\t" + c_fname);
String synapseFilePath = file.getParentFile().getPath() + NeuGenConstants.FILE_SEP + c_fname;
Writer synFW = new FileWriter(new File(synapseFilePath));
// write exp2 synapses
net.getHocData().writetohocExp2Synapses(fw, synFW);
fw.append("/* Input synapses. */ \n");
// write alpha synapses
net.getHocData().writetohocAlphaSynapses(fw);
fw.close();
// write model to file
String sm_fname = hocFileName + "model.hoc";
trigger.outPrintln("\t" + sm_fname);
String modelFilePath = file.getParentFile().getPath() + NeuGenConstants.FILE_SEP + sm_fname;
fw = new FileWriter(new File(modelFilePath));
fw.write("celsius = 37.0\n");
fw.append("wopen(\"" + hocFileName + "." + events_postfix + "\")\n");
fw.append("xopen(\"" + hocFileName + ".hoc\")\n\n\n");
//netConEvents.out (simone)
fw.append("objref list, netConList, netConI, tvec, idvec\n\n");
fw.append("tvec = new Vector() //vector for storing the times of recorded events\n");
fw.append("idvec = new Vector() //vector for storing the ids of recorded NetCons\n\n");
fw.append("//list of all NetCons\n");
fw.append("netConList = new List()\n\n");
List<Cons> synapseList = net.getSynapseList();
for (int n = 0; n < synapseList.size(); ++n) {
Cons synapse = synapseList.get(n);
//if index < 0 -> nf_synapse
if (synapse.getNeuron1() == null) {
continue;
}
fw.append("netConList.append(Nc" + n + ")" + "\n");
}
fw.append("\nproc mkmovie() {\n\n"
+ " print t\n\n"
+ " for i=0, netConList.count()-1 {\n"
+ " netConI = netConList.object(i)\n"
+ " netConI.record(tvec, idvec)\n"
+ " }\n\n"
+ " cvode.event(t+1, \"mkmovie()\")\n"
+ "}\n\n");
net.getHocData().writetohocModel(fw);
net.getHocData().writetohocChannels(fw);
fw.append("v_init = -70" + "\n"
+ "t = 0" + "\n"
+ "tstop = 100" + "\n"
+ "dt = 0.025" + "\n"
+ "steps_per_ms = 5" + "\n"
+ "realtime = 0" + "\n"
+ "run()\n"
+ "//write output file from recorded NetCon vectors\n"
+ "//each line contains the timestep and all ids of NetCons active in that timestep\n"
+ "for z=0, tstop{ \n"
+ " fprint(\"%d\" , z)\n"
+ " for tv=0, tvec.size()-1 {\n"
+ " if(z<=tvec.x[tv] && tvec.x[tv]<(z+1)){\n"
+ " fprint(\" %d% \", idvec.x[tv])\n"
+ " }\n"
+ " }\n"
+ " fprint(\"\\n\")\n"
+ "}\n\n"
+ "wopen()\n"
+ "quit()");
fw.flush();
fw.close();
}
public void exportNet() {
Writer fw = null;
String hoc = NeuGenConstants.EXTENSION_HOC;
String extension = Utils.getExtension(file);
if (!hoc.equals(extension)) {
file = new File(file.getAbsolutePath() + "." + hoc);
}
if (netConEventsFilePostfix.equals("")) {
netConEventsFilePostfix = null;
}
if (voltageFilePostfix.equals("")) {
voltageFilePostfix = null;
}
try {
trigger.outPrintln("Write hoc file for NEURON");
trigger.outPrintln("\t" + hocFileName + ".hoc");
fw = new FileWriter(file);
// (simone) only events
if (netConEventsFilePostfix != null && voltageFilePostfix == null) {
logger.info("write only events (net con events file postfix): " + netConEventsFilePostfix);
writetohocNetOnlyEvents(file, netConEventsFilePostfix, fw);
} //(simone) only voltages
else if (netConEventsFilePostfix == null && voltageFilePostfix != null) {
logger.info("write only voltages (voltage file postfix): " + voltageFilePostfix);
writetohocNet(fw);
} //(simone) voltages and events
else if (netConEventsFilePostfix != null && voltageFilePostfix != null) {
logger.info("write voltages and events: " + netConEventsFilePostfix + ", " + voltageFilePostfix);
writetohocNetVoltageAndEvents(fw);
} // default writetohoc
else {
voltageFilePostfix = "out";
logger.info("default write voltage: " + voltageFilePostfix);
writetohocNet(fw);
}
} catch (Exception e) {
logger.error("Could not create file: " + e, e);
}
}
public static void main(String args[]) {
MorphMLReader netBuilder = new MorphMLReader();
String fname = "bal.xml";
netBuilder.runMorphMLReader(fname);
Net net = netBuilder.getNet();
System.out.println("Size of the neural net: " + net.getNeuronList().size());
File file = new File("MyData.hoc");
HocWriter exportHoc = new HocWriter(net, file);
exportHoc.exportNet();
}
}