diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a158a8..1eec623 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,7 @@ jobs: - pyNeuroML_validate_sbml - jNeuroML_Moose - MOOSE:3.1.5 + - XPP steps: - uses: actions/checkout@v4 diff --git a/omv/common/inout.py b/omv/common/inout.py index 9a2e9ed..27cf80a 100644 --- a/omv/common/inout.py +++ b/omv/common/inout.py @@ -121,8 +121,8 @@ def check_output(cmds, cwd=".", shell=False, verbosity=0, env=None): except sp.CalledProcessError as err: inform( - "Error running commands: %s in %s (return code: %s)" - % (cmds, cwd, err.returncode), + "CalledProcessError running commands: %s in %s (return code: %s), output:\n%s" + % (cmds, cwd, err.returncode, err.output), indent=2, verbosity=verbosity, ) diff --git a/omv/engines/__init__.py b/omv/engines/__init__.py index d782602..4c43969 100644 --- a/omv/engines/__init__.py +++ b/omv/engines/__init__.py @@ -23,6 +23,7 @@ # from omv.engines.brian1 import Brian1Engine from omv.engines.brian2_ import Brian2Engine from omv.engines.arbor_ import ArborEngine +from omv.engines.xpp import XppEngine from omv.engines.eden_ import EdenEngine from omv.engines.nestsli import NestEngine from omv.engines.pynest import PyNestEngine diff --git a/omv/engines/getxpp.py b/omv/engines/getxpp.py new file mode 100644 index 0000000..4451b31 --- /dev/null +++ b/omv/engines/getxpp.py @@ -0,0 +1,83 @@ +import os +from omv.common.inout import inform, check_output + +from omv.engines.utils.wdir import working_dir +from sysconfig import get_paths +import sys + +import fileinput + + +def install_xpp(version='latest'): + + if version is None: + version='latest' + elif not version=='latest': + raise Exception('Can currently only install the latest XPP version') + + inform("Installing XPP", indent=2, verbosity=1) + xppinstallpath = os.path.join(os.environ["HOME"]) + xpphomepath = os.path.join(xppinstallpath, 'xppaut') + + inform( + "Installing XPP to: %s" % (xpphomepath), + indent=2, + verbosity=1, + ) + pypaths = get_paths() + inform("Python lib info: %s" % (pypaths), indent=2, verbosity=1) + + + with working_dir(xppinstallpath): + print( + check_output( + [ + "git", + "clone", + "https://github.com/NeuroML/xppaut" + ] + ) + ) + + with working_dir(xpphomepath): + print( + check_output( + [ + "ls", + "-alth" + ] + ) + ) + + makefile = os.path.join(xpphomepath, 'Makefile') + + print(' - Replacing text in %s'%makefile) + with open(makefile, 'r') as file: + filedata = file.read() + + # Replace the target string + filedata = filedata.replace("/usr/local/", "%s/"%xpphomepath) + + # Write the file out again + with open(makefile, 'w') as file: + file.write(filedata) + + print( + check_output( + [ + "make", "-j4" + ] + ) + ) + print( + check_output( + [ + "make", "install" + ] + ) + ) + + + +if __name__ == "__main__": + install_nest() diff --git a/omv/engines/xpp.py b/omv/engines/xpp.py new file mode 100644 index 0000000..0239af9 --- /dev/null +++ b/omv/engines/xpp.py @@ -0,0 +1,82 @@ +import os +import sys + +import subprocess as sp + +from omv.common.inout import inform, trim_path, check_output, is_verbose +from omv.engines.engine import OMVEngine, EngineExecutionError + + +class XppEngine(OMVEngine): + name = "XPP" + + @staticmethod + def get_xpp_environment(): + + # Default, if it was installed by omv... + xpppath = os.path.join(os.environ["HOME"], "xppaut/bin") + + if "XPP_HOME" in os.environ: + xpppath = os.environ["XPP_HOME"] + "/" + + environment_vars = { + "XPP_HOME": xpppath, + } + + return environment_vars + + @staticmethod + def is_installed(): + ret = True + environment_vars = XppEngine.get_xpp_environment() + try: + FNULL = open(os.devnull, "w") + + r = check_output( + [environment_vars["XPP_HOME"] + "/xppaut", "-version"], verbosity=2 + ) + if "Problem" in r: + ret = 'v???' + + else: + ret = "%s" % r.split()[2] + if not "v" in ret: + ret = "v%s" % ret + + inform("XPP %s is correctly installed..." % ret, indent=2, verbosity=1) + + except OSError as err: + if is_verbose(): + inform("Couldn't execute XPP: ", err, indent=1) + ret = False + return ret + + @staticmethod + def install(version): + from omv.engines.getxpp import install_xpp + + install_xpp(version) + inform("Done...", indent=2) + + def run(self): + self.environment_vars = XppEngine.get_xpp_environment() + self.set_environment() + + try: + inform( + "Running file %s with %s" % (trim_path(self.modelpath), self.name), + indent=1, + ) + self.stdout = check_output( + [self.environment_vars["XPP_HOME"] + "/xppaut", self.modelpath, '-silent'], + cwd=os.path.dirname(self.modelpath), + ) + self.returncode = 0 + except sp.CalledProcessError as err: + self.returncode = err.returncode + self.stdout = err.output + raise EngineExecutionError + except Exception as err: + inform("Another error with running %s: " % self.name, err, indent=1) + self.returncode = -1 + self.stdout = "???" diff --git a/omv/omv_util.py b/omv/omv_util.py index 3d3afc1..c81ce1f 100644 --- a/omv/omv_util.py +++ b/omv/omv_util.py @@ -279,6 +279,16 @@ def _install_engine(eng): install_arbor(engine_version) + elif eng.lower() == "XPP".lower(): + from omv.engines.xpp import XppEngine as ee + + if ee.is_installed(): + already_installed = True + else: + from omv.engines.getxpp import install_xpp + + install_xpp(engine_version) + elif eng.lower() == "EDEN".lower(): from omv.engines.eden_ import EdenEngine as ee diff --git a/setup.cfg b/setup.cfg index 44e4ee9..c66d04d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = OSBModelValidation -version = 0.2.20 +version = 0.2.21 author = Boris Marin, Padraig Gleeson author_email = borismarin@gmail.com url = https://github.com/OpenSourceBrain/osb-model-validation @@ -18,6 +18,7 @@ classifiers= Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Scientific/Engineering [options] diff --git a/utilities/tests/.test.xpp.mep b/utilities/tests/.test.xpp.mep new file mode 100644 index 0000000..1a79762 --- /dev/null +++ b/utilities/tests/.test.xpp.mep @@ -0,0 +1,7 @@ +system: Test XPP + +experiments: + spikes: + expected: + spike times: [32.25, 60.400002, 156.60001, 256.45001, 356.10001, 455.79999] + diff --git a/utilities/tests/.test.xpp.omt b/utilities/tests/.test.xpp.omt new file mode 100644 index 0000000..9be032c --- /dev/null +++ b/utilities/tests/.test.xpp.omt @@ -0,0 +1,17 @@ +target: nca.ode +engine: XPP + +mep: .test.xpp.mep +experiments: + spikes: + observables: + spike times: + file: + path: output.dat + columns: [0,1] + scaling: [1, 1] + spike detection: + method: threshold + threshold: 0 + tolerance: 0.00 + diff --git a/utilities/tests/nca.ode b/utilities/tests/nca.ode new file mode 100644 index 0000000..e6ce45f --- /dev/null +++ b/utilities/tests/nca.ode @@ -0,0 +1,82 @@ +# Excitatory cortical neurons, normal [Ca]. +# +number Cm=1.0 +number pms=3,pns=4 +number VNa=55.0,t_tauh=-40.5,t_taun=-27.0 +number thetaa=-50.0,sigmaa=20.0,thetab=-80.0,sigmab=-6.0,tauBs=15.0 +number thetam=-30.0,sigmam=9.5,sigmah=-7.0,sigman=10.0,sigmaz=5.0 +number VCa=120 +number thetar=-20.0,sigmar=10.0,thetac=-30,sigmac=7 +number pwrc=1,pwrq=4 +# +p gNa=35.0,gKdr=6.0,gL=0.05,Iapp=1.0 +p gA=1.4,gNaP=0.2,gZ=1.0 +p thetaz=-39.0,tauZs=75.0 +p phi=10.0, thetah=-45.0 +p thetan=-35.0,thetap=-41.0,sigmap=3.0 +p VK=-90.0,VL=-70.0 +p gCa=0.08,gKCa=10.0,gKAHP=5 +p tauRs=1.0,aq=2,ac=6,tauq=450,tauCa=13,uuCa=0.13,tauKc=2 +# +GAMMAF(VV,theta,sigma)=1.0/(1.0+exp(-(VV-theta)/sigma)) +ZFUNC(AA,CA,zz)=1/(1+(AA^zz/CA^zz)) +# +VVs'=(-gL*(VVs-VL)-INa-INaP-IKdr-IA-Iz-ICa-IKC-IAHP+Iappx)/Cm +hhs'=phi*(GAMMAF(VVs,thetah,sigmah)-hhs)/(1.0+7.5*GAMMAF(VVs,t_tauh,-6.0)) +nns'=phi*(GAMMAF(VVs,thetan,sigman)-nns)/(1.0+5.0*GAMMAF(VVs,t_taun,-15.0)) +bbs'=(GAMMAF(VVs,thetab,sigmab)-bbs)/tauBs +zzs'=(GAMMAF(VVs,thetaz,sigmaz)-zzs)/tauZs +rrs'=(GAMMAF(VVs,thetar,sigmar)-rrs)/tauRs +ccs'=(GAMMAF(VVs,thetac,sigmac)-ccs)/tauKc +qqs'=(ZFUNC(aq,Ca,pwrq)-qqs)/tauq +Ca'=-uuCa*ICa-Ca/tauCa +# +Iappx=Iapp +#if(t<=3.0)then(Iapp)else(0.0) +Minfs=GAMMAF(VVs,thetam,sigmam) +Pinfs=GAMMAF(VVs,thetap,sigmap) +Ainfs=GAMMAF(VVs,thetaa,sigmaa) +mKCa=ZFUNC(ac,Ca,pwrc) +# +INa=gNa*(Minfs^pms)*hhs*(VVs-VNa) +INaP=gNaP*Pinfs*(VVs-VNa) +IKdr=gKdr*(nns^pns)*(VVs-VK) +IA=gA*Ainfs^3*bbs*(VVs-VK) +Iz=gZ*zzs*(VVs-VK) +ICa=gCa*rrs^2*(VVs-VCa) +IKC=gKCa*mKCa*ccs*(VVs-VK) +IAHP=gKAHP*qqs*(VVs-VK) +# +VVs(0)=-71.962 +hhs(0)=0.979199 +nns(0)=0.0242166 +bbs(0)=0.207565 +zzs(0)=0.0013689 +Ca[0]=0.000787 +rrs(0)=0.005507 +ccs(0)=0.002486 +qqs(0)=0.0 +# +@ MAXSTOR=800000 +@ BACK=Black +@ XP=T +@ YP=VVs +@ AXES=2 +@ TOTAL=500.0 +@ DT=0.05 +@ NJMP=1 +@ T0=0.0 +@ TRANS=0.0 +@ NMESH=40 +@ METH=rungekutta +@ DTMIN=0.001 +@ DTMAX=1.0 +@ TOLER=0.00001 +@ BOUND=10000.0 +@ DELAY=0 +@ XLO=0.0, XHI=500.0, YLO=-90.0, YHI=30.0 +@ NTST=50,NMAX=2000,NPR=50 +@ DS=0.02,DSMIN=0.001,DSMAX=0.5 +@ PARMIN=-10,PARMAX=50,NORMMIN=0.0,NORMMAX=10000.0 +@ AUTOVAR=VVs1,AUTOXMIN=-10.0,AUTOXMAX=50.0,AUTOYMIN=-90.0,AUTOYMAX=50.0 +done