/*
* 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 com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
//import groovyjarjarantlr.PreservingFileWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.vecmath.Point3f;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
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.NetBase;
import org.neugen.datastructures.NeuronalTreeStructure;
import org.neugen.datastructures.neuron.Neuron;
import org.neugen.datastructures.Pair;
import org.neugen.datastructures.Section;
import org.neugen.datastructures.Segment;
import org.neugen.parsers.NeuroML.NetworkML.NeuroMLSynapse;
import org.neugen.parsers.NeuroML.NetworkML.NeuroMLSynapseBilateral;
import org.neugen.parsers.NeuroML.NetworkML.NeuroMLSynapseUnilateral;
import org.neugen.utils.Utils;
/**
*
* @author Alexander Wanner
* @author Sergei Wolf
*/
final class Neuroml {
public String xmlns;
public String mml;
public String meta;
public String bio;
public String cml;
public String xsi;
public String schemaLocation;
public String name;
public String lengthUnits;
/**
* Level 1: Anatomy only cells specified in MorphML Level 2: ChannelML
* and biophysical cell models Level 3: Networks of biologically
* realistic cells using NetworkML
*
* @param schemaLocation
* @param name
* @param lengthUnits
*
*/
public void setHeader(String schemaLocation, String name, String lengthUnits) {
xmlns = "http://morphml.org/morphml/schema";
mml = "http://morphml.org/morphml/schema";
bio = "http://morphml.org/biophysics/schema";
cml = "http://morphml.org/channelml/schema";
meta = "http://morphml.org/metadata/schema";
xsi = "http://www.w3.org/2001/XMLSchema-instance";
this.schemaLocation = schemaLocation;
this.name = name;
this.lengthUnits = lengthUnits;
}
}
/**
*
* Defines the smallest unit within a possibly branching structure, such as a
* dendrite or axon. The parent attribute is used to define connectivity. A
* segment would be mapped to a compartment in a compartmental modelling
* application such as GENESIS
*
*/
final class NeuroMLSegment {
/**
* The ID of the segment, which should be unique within the cell
*/
public int id;
/**
* A unique name can be given to the segment (Use: optional)
*/
public String name;
/**
* Used to indicate logical connectivity between segments (Use:
* optional)
*/
public Integer parent;
/**
* The cable ID of which this segment is part (Use: optional)
*/
public Integer cable;
/**
* The start point
*/
public Proximal proximal;
/**
* The end point
*/
public Distal distal;
}
/**
* Definition of a cell
*/
final class NeuroMLCell {
// a name can be given to the cell (Use: optional)
public String name;
// first segment should represent the soma
private NeuroMLSegments segments;
private NeuroMLCables cables;
public NeuroMLSegments getSegments() {
return segments;
}
public void setSegments(NeuroMLSegments segments) {
this.segments = segments;
}
public NeuroMLCables getCables() {
return cables;
}
public void setCables(NeuroMLCables cables) {
this.cables = cables;
}
}
final class NeuroMLCables {
private List<Cable> cables;
private String xmlns;
public NeuroMLCables(List<Cable> cables, String xmlns) {
this.cables = cables;
this.xmlns = xmlns;
}
}
final class NeuroMLSegments {
private List<NeuroMLSegment> segments;
private String xmlns;
public NeuroMLSegments(List<NeuroMLSegment> segments, String xmlns) {
this.segments = segments;
this.xmlns = xmlns;
}
}
/**
* NeuroML Level 2 Biophysics
*
* @author sergejwolf
*
*/
// TODO:
final class NeuroMLBiophysics {
private BioSpecCapacitance specificCapacitance;
}
final class BioSpecCapacitance {
protected BioSpec parameter;
}
final class BioSpecAxialResistance {
private BioSpec parameter;
}
final class BioSpec {
private int value;
private String group;
}
// TODO:
final class NeuroMLChannel {
}
/**
*
* The start point (and diameter) of the segment. If absent, it is assumed that
* the distal point of the parent is the start point of this segment.
*
*/
final class Proximal {
private Float x, y, z, diameter;
public Proximal(Float x, Float y, Float z, Float diam) {
this.x = x;
this.y = y;
this.z = z;
this.diameter = diam;
}
}
/**
*
* The end point (and diameter) of the segment. Note if the 3D location of the
* distal point is the same as the proximal point, the segment is assumed to be
* spherical.
*/
final class Distal {
private Float x, y, z, diameter;
public Distal(Float x, Float y, Float z, Float diam) {
this.x = x;
this.y = y;
this.z = z;
this.diameter = diam;
}
}
class Cable {
private int id;
public Cable(int id, String name) {
this.id = id;
this.name = name;
}
public String name;
// public int parent;
public String metaGroup;
}
final class CableFAP extends Cable {
public CableFAP(int id, String name) {
super(id, name);
}
public int fractAlongParent;
}
public final class NeuroMLWriter {
private static final long serialVersionUID = 1L;
/**
* use to log messages
*/
private final static Logger logger = Logger.getLogger(NeuroMLWriter.class.getName());
//private int segId = 0;
//protected XMLGeometry geometry;
protected final int d = 3;
private static XStream xstream;
//private int sectionID = 0;
private int neuromlLevel = 1;
private void initXStream() {
xstream = new XStream(new DomDriver());
//xstream.setMode(XStream.NO_REFERENCES);
}
public int getNeuromlLevel() {
return neuromlLevel;
}
public void setNeuromlLevel(int neuromlLevel) {
this.neuromlLevel = neuromlLevel;
}
/**
* Function to get neuroml output options
*
* @return xstream object with output options for xml format
*/
private void setXStreamOptions(int level) {
// aliases for class names
xstream.alias("segment", NeuroMLSegment.class);
xstream.alias("segments", NeuroMLSegments.class);
xstream.addImplicitCollection(NeuroMLSegments.class, "segments");
xstream.alias("proximal", Proximal.class);
xstream.alias("distal", Distal.class);
xstream.alias("cell", NeuroMLCell.class);
xstream.alias("morphml", Neuroml.class);
xstream.alias("cable", NeuroMLCables.class);
xstream.addImplicitCollection(NeuroMLCables.class, "cables");
xstream.alias("cable", Cable.class);
xstream.alias("cable", CableFAP.class);
// aliases for variables
xstream.aliasAttribute(Neuroml.class, "mml", "\n\txmlns:mml");
xstream.aliasAttribute(Neuroml.class, "meta", "\n\txmlns:meta");
xstream.aliasAttribute(Neuroml.class, "xsi", "\n\txmlns:xsi");
xstream.aliasAttribute(Neuroml.class, "bio", "\n\txmlns:bio");
xstream.aliasAttribute(Neuroml.class, "cml", "\n\txmlns:cml");
xstream.aliasAttribute(Neuroml.class, "schemaLocation", "\n\txsi:schemaLocation");
xstream.aliasAttribute(Neuroml.class, "name", "\n\tname");
xstream.aliasAttribute(Neuroml.class, "lengthUnits", "\n\tlengthUnits");
/*
if(level == 1) {
xstream.omitField(Neuroml.class, "bio");
xstream.omitField(Neuroml.class, "cml");
}
*/
// set the Neuroml memeber variables as na XML attribute
xstream.useAttributeFor(Neuroml.class, "xmlns");
xstream.useAttributeFor(Neuroml.class, "mml");
xstream.useAttributeFor(Neuroml.class, "meta");
xstream.useAttributeFor(Neuroml.class, "bio");
xstream.useAttributeFor(Neuroml.class, "cml");
xstream.useAttributeFor(Neuroml.class, "xsi");
xstream.useAttributeFor(Neuroml.class, "schemaLocation");
xstream.useAttributeFor(Neuroml.class, "name");
xstream.useAttributeFor(Neuroml.class, "lengthUnits");
//set the NeuroML member variables as an XML attribute
xstream.useAttributeFor(NeuroMLSegment.class, "id");
xstream.useAttributeFor(NeuroMLSegment.class, "cable");
xstream.useAttributeFor(NeuroMLSegment.class, "parent");
xstream.useAttributeFor(NeuroMLSegment.class, "name");
xstream.useAttributeFor(NeuroMLSegments.class, "xmlns");
//set the Proximal member variables as an XML attribute
xstream.useAttributeFor(Proximal.class, "x");
xstream.useAttributeFor(Proximal.class, "y");
xstream.useAttributeFor(Proximal.class, "z");
xstream.useAttributeFor(Proximal.class, "diameter");
//set the Distal member variables as an XML attribute
xstream.useAttributeFor(Distal.class, "x");
xstream.useAttributeFor(Distal.class, "y");
xstream.useAttributeFor(Distal.class, "z");
xstream.useAttributeFor(Distal.class, "diameter");
xstream.useAttributeFor(NeuroMLCell.class, "name");
xstream.useAttributeFor(NeuroMLCables.class, "xmlns");
xstream.useAttributeFor(Cable.class, "id");
xstream.useAttributeFor(CableFAP.class, "fractAlongParent");
xstream.aliasAttribute(Cable.class, "name", "name");
xstream.aliasField("meta:group", Cable.class, "metaGroup");
}
public static XStream getXstream() {
return xstream;
}
private void copyToNeuroML(List<Section> sections, List<NeuroMLSegment> neuroMLList, List<Cable> cables, int nn, String cableName) {
Point3f sstart;
Point3f send;
float sradiusstart, sradiusend;
Map<Section, Pair<Integer, Integer>> sectionToStartEndSegment = new HashMap<Section, Pair<Integer, Integer>>();
// fuelle die Liste: jetzt ist nur die erste Sektion des Teilbaumes in der Liste
Section.Iterator secIterator = sections.get(0).getIterator();
//Section section = secIterator.getNext();
sections.clear();
while (secIterator.hasNext()) {
Section section = secIterator.next();
sections.add(section);
//section = secIterator.next();
}
for (Section section : sections) {
//logger.info("section id: " + section.getId() + " | section name: " + section.getName());
float fractAlongParent = -1.0f;
List<Segment> segments = section.getSegments();
if (segments.size() > 0) {
int startSegId = segments.get(0).getId();
int endSegId = startSegId;
if (segments.size() > 0) {
endSegId = segments.get((segments.size() - 1)).getId();
}
sectionToStartEndSegment.put(section, new Pair<Integer, Integer>(startSegId, endSegId));
int counter = 0;
for (Segment segment : segments) {
sstart = segment.getStart();
send = segment.getEnd();
sradiusstart = segment.getStartRadius();
sradiusend = segment.getEndRadius();
NeuroMLSegment neuroMLSegment = new NeuroMLSegment();
neuroMLSegment.id = segment.getId();
//logger.info("segment id: " + segment.getSegmentId());
//neuroMLSegment.name = segment.getName();
//logger.info("name of this segment: " + segment.getName());
//neuroMLSegment.id = segId;
neuroMLSegment.cable = section.getId();
//neuroMLSegment.name = "N" + nn + cableName + sectionId + "_seg_id_" + segmentId;
neuroMLSegment.name = segment.getName();
if (segment.getName() == null) {
neuroMLSegment.name = "N" + nn + cableName + section.getId() + "_seg_id_" + segment.getId();
}
if (counter == 0) {
if (section.getParentalLink() != null && section.getParentalLink().getParental() != null) {
Section parentSection = section.getParentalLink().getParental();
fractAlongParent = parentSection.getFractAlongParentForChild(section);
int parentID = -1;
if (fractAlongParent > 0.0f) {
parentID = sectionToStartEndSegment.get(parentSection).second;
//logger.info("parentID if: " + parentID);
} else {
parentID = sectionToStartEndSegment.get(parentSection).first;
//logger.info("parentID else: " + parentID);
}
neuroMLSegment.parent = parentID;
}
} else {
neuroMLSegment.parent = neuroMLSegment.id - 1;
}
if (counter == 0) {
neuroMLSegment.proximal = new Proximal(sstart.x, sstart.y, sstart.z, sradiusstart * 2);
}
neuroMLSegment.distal = new Distal(send.x, send.y, send.z, sradiusend * 2);
neuroMLList.add(neuroMLSegment);
counter++;
}
int sectionID = section.getId();
Cable cable = null;
if (fractAlongParent > -1.0f) {
//CableFAP cableFAP = new CableFAP(sectionID, "N" + nn + cableName + sectionID);
CableFAP cableFAP = new CableFAP(sectionID, section.getName());
cableFAP.fractAlongParent = (int) fractAlongParent;
cable = cableFAP;
} else {
//cable = new Cable(sectionID, "N" + nn + cableName + sectionID);
cable = new Cable(sectionID, section.getName());
}
cables.add(cable);
//sectionID++;
//sectionID = section.getSectionId();
/*// durchlaufe die Segmenten der Section
// all segments of a section
Section.Iterator secIterator = firstSection.getIterator();
while (secIterator.hasNext()) {
Section section = secIterator.next();
String cableName = "_Section_";
float typeID = section.getTypeID();
if (geometry.isSomaID(typeID)) {
cableName = "soma";
} else if (geometry.isAxon(typeID)) {
cableName = "axon";
}
else if(geometry.isAxonHillocID(typeID)) {
cableName = "_axon_hilloc_";
}
else if (geometry.isAxonInitialSegmentID(typeID)) {
cableName = "axon_initial_segment_group";
} else if (geometry.isAxonMyalinizedID(typeID)) {
cableName = "axon_myel_group";
} else if (geometry.isAxonRanvierID(typeID)) {
cableName = "axon_ranvier_group";
} else if (geometry.isDendrite(typeID)) {
cableName = "dendrite";
}*/
/*//float typeID = section.getTypeID();
if (geometry.isSomaID(typeID)) {
cable.metaGroup = "soma_group";
} else if (geometry.isAxon(typeID)) {
cable.metaGroup = "axon_group";
}*/
/*
else if(geometry.isAxonHillocID(typeID)) {
cable.metaGroup = "axon_hilloc_group";
} else if(geometry.isAxonInitialSegmentID(typeID)) {
cable.metaGroup = "axon_initial_segment_group" ;
} else if(geometry.isAxonMyalinizedID(typeID) ) {
cable.metaGroup = "axon_myel_group";
} else if(geometry.isAxonRanvierID(typeID)) {
cable.metaGroup = "axon_ranvier_group";
}
*/
/*else if (geometry.isDendrite(typeID)) {
cable.metaGroup = "dendrite_group";
}*/
// cables.add(cable);
// sectionID++;
}
}
// return neuroMLList;
}
/**
* @brief write the synapses (unilateral (nf synapses) and bilateral
* (functional synapses))
* @author stephanmg
* @param oos
* @param net
*/
private void writeXMLSynapses(ObjectOutputStream oos, Net net) {
List<Cons> synapseList = net.getSynapseList();
logger.info("Number of synapses: " + synapseList.size());
ArrayList<NeuroMLSynapse> neuroMLSynapses = new ArrayList<NeuroMLSynapse>();
/// iterate over all synapses in the network
for (Cons synapse : synapseList) {
if (synapse.getNeuron1() == null && synapse.getNeuron2() == null) {
continue;
}
if (synapse.getNeuron1() == null && synapse.getNeuron2() != null) {
/// unilateral synapse
Point3f injection = synapse.getNeuron2DenSectionSegment().getEnd();
neuroMLSynapses.add(new NeuroMLSynapseUnilateral(injection));
} else if (synapse.getNeuron1() != null && synapse.getNeuron2() == null) {
/// unilateral synapse
} else {
/// functional/bilateral synapse
Point3f axon_end = synapse.getNeuron1AxSegment().getEnd(); /// Start of synapse
Point3f dendrite_start = synapse.getNeuron2DenSectionSegment().getStart(); /// End of synapse
neuroMLSynapses.add(new NeuroMLSynapseBilateral(axon_end, dendrite_start));
}
}
NeuroMLWriterTask task = NeuroMLWriterTask.getInstance("Synapses");
task.setMyProgress(0.0f);
int i = 0;
for (NeuroMLSynapse synapse : neuroMLSynapses) {
logger.info("Synapse: " + synapse.toString());
try {
oos.writeObject(synapse);
oos.flush();
} catch (IOException e) {
logger.error(e, e);
}
if (task != null) {
i++;
logger.info("progress: " + i / (float) neuroMLSynapses.size());
task.setMyProgress( i / (float) neuroMLSynapses.size());
}
}
if (task != null) {
task.setMyProgress(1.0f);
}
}
/**
* Function to write the neural net to a XML file
*/
private void writeXMLNet(ObjectOutputStream oos, Net net) {
// get the number of neurons from neural net
int nneuron = net.getNumNeurons();
String schema = "http://morphml.org/morphml/schema";
NeuroMLWriterTask task = NeuroMLWriterTask.getInstance("Neurons");
for (int i = 0; i < nneuron; i++) {
Neuron neuron = net.getNeuronList().get(i);
NeuroMLCell neuroMLCell = new NeuroMLCell();
neuroMLCell.name = "N" + i + "soma";
//sets soma, dendrite and axon
List<Cable> cables = new ArrayList<Cable>();
List<NeuroMLSegment> segments = new ArrayList<NeuroMLSegment>();
Cellipsoid soma = neuron.getSoma();
List<Section> sections = soma.getSections();
if (soma.getSections().isEmpty()) {
sections.add(neuron.getSoma().cylindricRepresentant());
}
// soma first
copyToNeuroML(sections, segments, cables, i, "_soma_");
sections.clear();
Axon axon = neuron.getAxon();
sections.add(axon.getFirstSection());
copyToNeuroML(sections, segments, cables, i, "_axon_");
sections.clear();
List<Dendrite> dendritesList = neuron.getDendrites();
for (Dendrite dendrite : dendritesList) {
sections.add(dendrite.getFirstSection());
copyToNeuroML(sections, segments, cables, i, "_dendrite_");
sections.clear();
}
// add soma, dendrite and axon to XML
NeuroMLSegments neuroMLSegments = new NeuroMLSegments(segments, schema);
neuroMLCell.setSegments(neuroMLSegments);
NeuroMLCables neuroMLCables = new NeuroMLCables(cables, schema);
neuroMLCell.setCables(neuroMLCables);
try {
oos.writeObject(neuroMLCell);
oos.flush();
//oos.close();
} catch (IOException e) {
logger.error(e, e);
}
if (task != null) {
float process = (float) (i + 1) / (float) nneuron;
task.setMyProgress(process);
}
//trigger.trigger((i + 1) / (float) nneuron);
}
if (task != null) {
task.setMyProgress(1.0f);
}
}
/**
* @brief exports the NeuroML data, i. e. we need also to export the
* synapses below
* @param file
* @param net
* @throws IOException
*/
public static void exportData(File file, Net net) throws IOException {
NeuroMLWriter exportML = new NeuroMLWriter();
exportML.initXStream();
int level = exportML.getNeuromlLevel();
exportML.setXStreamOptions(level);
String head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
String name = "NeuroML Level " + level + " file exported from NeuGen";
String lengthUnits = "micron";
String schemaLocation = "";
if (level == 1) {
schemaLocation = "http://morphml.org/neuroml/schema NeuroML_Level1_v1.7.xsd";
} else if (level == 2) {
schemaLocation = "http://morphml.org/neuroml/schema NeuroML_Level2_v1.7.xsd";
} else if (level == 3) {
schemaLocation = "http://morphml.org/networkml/schema../../Schemata/v1.7/Level3/NetworkML_v1.7.xsd";
} else {
}
Neuroml neuroml = new Neuroml();
neuroml.setHeader(schemaLocation, name, lengthUnits);
try {
String xml = "xml";
String extension = Utils.getExtension(file);
File net_output = null;
File synapses_output = null;
if (!xml.equals(extension)) {
net_output = new File(file.getAbsolutePath() + "." + xml);
synapses_output = new File(file.getAbsoluteFile() + "_synapses." + xml);
}
//trigger.trigger(0.0f);
//trigger.trigger("\n write xml file" + "\n");
//trigger.trigger(" " + file.getName());
FileWriter writer = new FileWriter(net_output);
writer.write(head);
PrettyPrintWriter writerP = new PrettyPrintWriter(writer);
writerP.startNode("neuroml");
writerP.addAttribute("xmlns", neuroml.xmlns);
writerP.addAttribute("\n\t\t xmlns:mml", neuroml.mml);
writerP.addAttribute("\n\t\t xmlns:meta", neuroml.meta);
writerP.addAttribute("\n\t\t xmlns:bio", neuroml.bio);
writerP.addAttribute("\n\t\t xmlns:cml", neuroml.cml);
writerP.addAttribute("\n\t\t xmlns:xsi", neuroml.xsi);
writerP.addAttribute("\n\t\t xsi:schemaLocation", schemaLocation);
writerP.addAttribute("\n\t\t name", name);
writerP.addAttribute("\n\t\t lengthUnits", lengthUnits);
XStream xstreamLoc = getXstream();
ObjectOutputStream oos = xstreamLoc.createObjectOutputStream(writerP, "cells");
exportML.writeXMLNet(oos, net);
if (level < 2) {
/**
* @todo proper xml output
*/
XStream xstreamLoc2 = getXstream();
FileWriter fw = new FileWriter(synapses_output);
fw.write(head);
PrettyPrintWriter writerP2 = new PrettyPrintWriter(fw);
ObjectOutputStream oos2 = xstreamLoc2.createObjectOutputStream(writerP2, "synapses");
exportML.writeXMLSynapses(oos2, net);
writerP2.endNode();
}
writerP.endNode();
oos.flush();
oos.close();
} catch (Exception e) {
logger.error(e, e);
}
}
}