diff --git a/Dockerfile b/Dockerfile index 4ab0b4ee4e..fb681a4c07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,8 +67,6 @@ COPY ./vcell-client/target/vcell-client-0.0.1-SNAPSHOT.jar \ ./vcell-admin/target/maven-jars/*.jar \ ./vcell-cli/target/vcell-cli-0.0.1-SNAPSHOT.jar \ ./vcell-cli/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./non-maven-java-libs/org/sbml/libcombine/libCombineLinux64/0.2.7/libCombineLinux64-0.2.7.jar \ /usr/local/app/vcell/lib/ diff --git a/README_Debugging.md b/README_Debugging.md index 10eb6369c8..ec1ed1ad84 100644 --- a/README_Debugging.md +++ b/README_Debugging.md @@ -3,7 +3,7 @@ If VCell client simulation list view says 'hasdata=no' but there is data -----Find sim id (click 'i' button when sim is selected) -> theSimID -----log into vcell-node1 (or any node not in DMZ, not vcellapi or vcellapi-beta) -----Check data exists, give cmd " ls /share/apps/vcell3/users/boris/SimID_theSimID* " ------open oracle db tool (toad,sql,squirrel), log into vcell@vcell-db.cam.uchc.edu and do query " select * from vc_simulationjob where simref=theSimID; " +-----open oracle db tool (toad,sql,squirrel), log into vcell@vcell-oracle.cam.uchc.edu and do query " select * from vc_simulationjob where simref=theSimID; " -----If the query column 'hasdata' is blank then do update " update vc_simulationJob set hasdata='Y' where simref=theSimID; " and " commit; " -----log into vcellapi(Rel) or vcellapi-beta(Alpha) -----Restart VCell docker scheduler service with cmd " sudo docker service update --force --detach=false vcell{rel,alpha}_sched " @@ -131,7 +131,7 @@ client local run alpha -Dvcell.server.dbPassword= -Dvcell.server.dbUserid=vcell -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver --Dvcell.server.dbConnectURL=jdbc:oracle:thin:@vcell-db.cam.uchc.edu:1521:vcelldborcl +-Dvcell.server.dbConnectURL=jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521:ORCLPDB1 -Dvcell.server.id=alpha\_7.0.0\_51 -Dvcell.mongodb.database=TEST -Dvcell.mongodb.host.internal=vcellapi-beta.cam.uchc.edu diff --git a/README_flow_control.md b/README_flow_control.md index ac24c42f79..2e8cf4f062 100644 --- a/README_flow_control.md +++ b/README_flow_control.md @@ -55,7 +55,7 @@ all solvers call vcell-solver/VCellMessaging/src/SimulationMessaging.cpp.setWork --SimulationDispatcherEngine.onworkerEvent(...) ---SimulationStateMachine.onWorkerEvent(...) ----SimulationDatabaseDirect.updateSimulationJobStatus(...) ------AdminDBToplevel.updateSimulationJobStatus(...) (**updates Oracle** real database host vcell-db.cam.uchc.edu, NOT vcell-db container) +-----AdminDBToplevel.updateSimulationJobStatus(...) (**updates Oracle** real database host vcell-oracle.cam.uchc.edu, NOT vcell-db container) ----StatusMessage.sendToClient(...) (sends to AMQint container clientStatus topic) **activemqint container** -recieves sim status message on the 'clientStatus' topic diff --git a/README_orphan.md b/README_orphan.md index 73cca01722..2387187743 100644 --- a/README_orphan.md +++ b/README_orphan.md @@ -79,21 +79,21 @@ vcellNagios.sh calls Rel web service https://vcellapi/health?check={login,sim} **DBBackupAndClean** vcell-node1:/opt/build/frm/dbbackupclean //Normal, without debug -java -cp ./maven-jars/*:./vcell-server-0.0.1-SNAPSHOT.jar:./ojdbc6-11.2.0.4.jar:./ucp-11.2.0.4.jar:./vcell-oracle-0.0.1-SNAPSHOT.jar -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver cbit.vcell.modeldb.DBBackupAndClean delsimsdisk vcell-db vcell /opt/build/frm/dbpw.txt vcelldborcl.cam.uchc.edu /tmp /share/apps/vcell3/users +java -cp ./maven-jars/*:./vcell-server-0.0.1-SNAPSHOT.jar:./ojdbc6-11.2.0.4.jar:./ucp-11.2.0.4.jar:./vcell-oracle-0.0.1-SNAPSHOT.jar -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver cbit.vcell.modeldb.DBBackupAndClean delsimsdisk vcell-oracle vcell /opt/build/frm/dbpw.txt ORCLPDB1 /tmp /share/apps/vcell3/users //Normal, without debug, single user only -java -Xms200m -Xmx1500m -cp ./maven-jars/*:./vcell-server-0.0.1-SNAPSHOT.jar:./ojdbc6-11.2.0.4.jar:./ucp-11.2.0.4.jar:./vcell-oracle-0.0.1-SNAPSHOT.jar -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver cbit.vcell.modeldb.DBBackupAndClean delsimsdisk2 vcell-db vcell /opt/build/frm/dbpw.txt vcelldborcl.cam.uchc.edu /tmp /share/apps/vcell3/users Juliajessica +java -Xms200m -Xmx1500m -cp ./maven-jars/*:./vcell-server-0.0.1-SNAPSHOT.jar:./ojdbc6-11.2.0.4.jar:./ucp-11.2.0.4.jar:./vcell-oracle-0.0.1-SNAPSHOT.jar -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver cbit.vcell.modeldb.DBBackupAndClean delsimsdisk2 vcell-oracle vcell /opt/build/frm/dbpw.txt ORCLPDB1 /tmp /share/apps/vcell3/users Juliajessica //With debug (note: eclipse debug config: vcell-server,vcell-node1,30301) -java -agentlib:jdwp=transport=dt_socket,server=y,address=30301,suspend=y -cp ./maven-jars/*:./vcell-server-0.0.1-SNAPSHOT.jar:./ojdbc6-11.2.0.4.jar:./ucp-11.2.0.4.jar:./vcell-oracle-0.0.1-SNAPSHOT.jar -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver cbit.vcell.modeldb.DBBackupAndClean delsimsdisk vcell-db vcell /opt/build/frm/dbpw.txt vcelldborcl.cam.uchc.edu /tmp /share/apps/vcell3/users +java -agentlib:jdwp=transport=dt_socket,server=y,address=30301,suspend=y -cp ./maven-jars/*:./vcell-server-0.0.1-SNAPSHOT.jar:./ojdbc6-11.2.0.4.jar:./ucp-11.2.0.4.jar:./vcell-oracle-0.0.1-SNAPSHOT.jar -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver cbit.vcell.modeldb.DBBackupAndClean delsimsdisk vcell-oracle vcell /opt/build/frm/dbpw.txt ORCLPDB1 /tmp /share/apps/vcell3/users -//DBBackupAndClean Scheduled Task (vcell-db.cam.uchc.edu->'scheduled task'->'Task Scheduler Library'->cleanAndBackupDB->properties->Actions->'Edit...') +//DBBackupAndClean Scheduled Task (vcell-oracle.cam.uchc.edu->'scheduled task'->'Task Scheduler Library'->cleanAndBackupDB->properties->Actions->'Edit...') Program/script: "C:\Program Files (x86)\Java\jre1.8.0_121\bin\java.exe" -Add arguments: -jar DBBackupAndClean_Ampli.jar cleanandbackup vcell-db vcell cbittech vcelldborcl.cam.uchc.edu \\cfs05\vcell\temp \\cfs03\shared\archive\vcell\VCDBdumps +Add arguments: -jar DBBackupAndClean_Ampli.jar cleanandbackup vcell-oracle vcell password ORCLPDB1 \\cfs05\vcell\temp \\cfs03\shared\archive\vcell\VCDBdumps Start in: C:\fromDBS3 -**Delete large listener.log file** +**Delete large listener.log file - obsolete - oracle now containerized running on vcell-oracle machine ** log in to vcell-db (a windows host) as admin user using 'Remote Desktop Connection' start 'cmd' window as system administrator (start->'command prompt->'rt-click, 'run as administrator') //make sure of path to listener log @@ -130,22 +130,22 @@ NOTE: enter following after 'java' for debug (-agentlib:jdwp=transport=dt_socket 4a. Run to generate .csv report, expects vcellDB password to be enetered on console java -cp ./dbbackupclean/ojdbc6-11.2.0.4.jar:./dbbackupclean/ucp-11.2.0.4.jar:./dbbackupclean/vcell-oracle-0.0.1-SNAPSHOT.jar:./cbit_vcell_tools_IonItems.jar - cbit.vcell.tools.IonItems 'jdbc:oracle:thin:@vcell-db.cam.uchc.edu:1521:vcelldborcl' '/share/apps/vcell3/users' publicmodelwithsims false | tee ./pubModelsSimIdOnly.txt + cbit.vcell.tools.IonItems 'jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521:ORCLPDB1' '/share/apps/vcell3/users' publicmodelwithsims false | tee ./pubModelsSimIdOnly.txt 4b. Run to save vcml of published modesl with sims that have no data or exceptions removed from vcml java -cp ./dbbackupclean/ojdbc6-11.2.0.4.jar:./dbbackupclean/ucp-11.2.0.4.jar:./dbbackupclean/vcell-oracle-0.0.1-SNAPSHOT.jar:./cbit_vcell_tools_IonItems.jar:./maven- jars/jhdf5_2.10-2.9.jar - -Djava.library.path="./natlibs" cbit.vcell.tools.IonItems "jdbc:oracle:thin:@vcell-db.cam.uchc.edu:1521:vcelldborcl" "/share/apps/vcell3/users" publishedmodelvcml /share/apps/vcell3/users/ionvcml 0 0 + -Djava.library.path="./natlibs" cbit.vcell.tools.IonItems "jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521:ORCLPDB1" "/share/apps/vcell3/users" publishedmodelvcml /share/apps/vcell3/users/ionvcml 0 0 4c. Run to read vcml saved from 4b and compare to vcml from db and print the sims that had to be removed java -cp ./dbbackupclean/ojdbc6-11.2.0.4.jar:./dbbackupclean/ucp-11.2.0.4.jar:./dbbackupclean/vcell-oracle-0.0.1-SNAPSHOT.jar:./cbit_vcell_tools_IonItems.jar:./maven-jars/jhdf5_2.10-2.9.jar - -Djava.library.path="./natlibs" cbit.vcell.tools.IonItems "jdbc:oracle:thin:@vcell-db.cam.uchc.edu:1521:vcelldborcl" /share/apps/vcell3/users/ionvcml publishedmodelvcmldiff + -Djava.library.path="./natlibs" cbit.vcell.tools.IonItems "jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521:ORCLPDB1" /share/apps/vcell3/users/ionvcml publishedmodelvcmldiff 4d. Run to save vcml from TestSuit java -cp ./dbbackupclean/ojdbc6-11.2.0.4.jar:./dbbackupclean/ucp-11.2.0.4.jar:./dbbackupclean/vcell-oracle-0.0.1-SNAPSHOT.jar:./cbit_vcell_tools_IonItems.jar:./maven-jars/jhdf5_2.10-2.9.jar - -Djava.library.path="./natlibs" cbit.vcell.tools.IonItems "jdbc:oracle:thin:@vcell-db.cam.uchc.edu:1521:vcelldborcl" /share/apps/vcell3/users/ionts testsuitemodelvcml + -Djava.library.path="./natlibs" cbit.vcell.tools.IonItems "jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521:ORCLPDB1" /share/apps/vcell3/users/ionts testsuitemodelvcml ***----------------------------------------------------------------------------------------------------------*** diff --git a/docker/README_Oracle.md b/docker/README_Oracle.md new file mode 100644 index 0000000000..6167a49500 --- /dev/null +++ b/docker/README_Oracle.md @@ -0,0 +1,71 @@ +# grab dump from VCell-db.cam.uchc.edu, windows 2008, Oracle 11g enterprise +### character set is WE8MSWIN1252 + +### export from vcell-db using datapump for vcell schema +in vcell-db database, named directory ```E_DATA_PUMP_DIR``` maps to ```e:\db``` + +scn = 1430710053 (compute from sql below) +```sql +select current_scn from v$database +``` +expdp system/<>@vcelldborcl directory=E_DATA_PUMP_DIR dumpfile=vcelldb_dump_2023_08_19.dmp logfile=vcelldb_dump_2023_08_19.log schemas=vcell flashback_scn=1430710053 + +# build new oracle database image on vcell-oracle.cam.uchc.edu + +### check out github repo for oracle docker containers +### download ```LINUX.X64_193000_db_home.zip``` and install in ./19.3.0/ directory +### build the container (from docker-images/OracleDatabase/SingleInstance/) + +```bash +./buildContainerImage.sh -s -v 19.3.0 -o '--build-arg SLIMMING=false' +``` +### tag and push image to container repository +```bash +docker tag <> ghcr.io/virtualcell/oracle-database:19.3.0-se2 +docker push ghcr.io/virtualcell/oracle-database:19.3.0-se2 +``` + +## create /data/oradata, /data/backup directories and set permissions to allow container oracle/dba rw access. + +## running the container on vcell-oracle (for the first time) +```bash +sudo docker run --detach --restart=always \ + --name oracle-database \ + -p 1521:1521 -p 5500:5500 \ + --ulimit nofile=1024:65536 --ulimit nproc=2047:16384 --ulimit stack=10485760:33554432 --ulimit memlock=3221225472 \ + -e ORACLE_SID=ORCLCDB \ + -e ORACLE_PDB=ORCLPDB1 \ + -e ORACLE_PWD=<> \ + -e INIT_CPU_COUNT=4 \ + -e ORACLE_EDITION=standard \ + -e ENABLE_TCPS=false \ + -e ORACLE_CHARACTERSET=WE8MSWIN1252 \ + -e ENABLE_ARCHIVELOG=true \ + -v /data/oradata:/opt/oracle/oradata \ + -v /data/backup:/opt/oracle/backup \ + ghcr.io/virtualcell/oracle-database:19.3.0-se2 +``` + +```bash +sudo docker logs -f oracle-database +``` + +## register directory object EXT_DATA_PUMP_DIR for /opt/oracle/backup (host /data/backup) +```sql +create or replace directory EXT_DATA_PUMP_DIR as '/opt/oracle/backup'; +``` + +## add user vcell (((Command doesn't work))) +sqlplus system/<>@ORCLPDB1 +```sql +create directory EXT_DATA_PUMP_DIR as '/opt/oracle/backup'; +``` + +## run the import +```bash +docker exec -it oracle-database /bin/bash +impdp system/<>@ORCLPDB1 schemas=vcell table_exists_action=REPLACE directory=EXT_DATA_PUMP_DIR dumpfile=FRMDEV2ORCL_VCELL_2023_08_19_17_05_08.DMP logfile=FRMDEV2ORCL_VCELL_2023_08_19_17_05_08.log + +impdp system/<>@ORCLPDB1 schemas=vcell table_exists_action=REPLACE directory=EXT_DATA_PUMP_DIR dumpfile=FRMDEV2ORCL_VCELL_2023_08_19_17_05_08.DMP logfile=FRMDEV2ORCL_VCELL_2023_08_19_17_05_08.log.3 +``` + diff --git a/docker/README_serviceInfo.md b/docker/README_serviceInfo.md index 1905779400..d782ae3e80 100644 --- a/docker/README_serviceInfo.md +++ b/docker/README_serviceInfo.md @@ -107,7 +107,7 @@ nmym8qspowiu1d850ok9vwtk2 vcell-node4.cam.uchc.edu Ready A kn3y3t1rw8skyua80j841ohng vcellapi-beta.cam.uchc.edu Ready Active Reachable 18.03.0-ce ``` -## vcell-db.cam.uchc.edu, NOT Docker, (VCell database) +## vcell-oracle.cam.uchc.edu, NOT Docker, (VCell database) Oracle database, has all models, sim status, all user data, everything ## vcell-docker.cam.uchc.edu, NOT Swarm node, (VCell docker registry) [details](build/README_Registry.md) diff --git a/docker/build/Dockerfile-admin-dev b/docker/build/Dockerfile-admin-dev index c81116f59e..c9c0430d24 100644 --- a/docker/build/Dockerfile-admin-dev +++ b/docker/build/Dockerfile-admin-dev @@ -3,15 +3,17 @@ FROM ubuntu:18.04 RUN apt-get -y update && \ apt-get install -y curl openjdk-8-jre dnsutils && \ mkdir -p /usr/local/app/lib - + +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime + WORKDIR /usr/local/app COPY ./vcell-server/target/vcell-server-0.0.1-SNAPSHOT.jar \ ./vcell-server/target/maven-jars/*.jar \ ./vcell-oracle/target/vcell-oracle-0.0.1-SNAPSHOT.jar \ ./vcell-oracle/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./vcell-api/target/vcell-api-0.0.1-SNAPSHOT.jar \ ./vcell-api/target/maven-jars/*.jar \ ./vcell-admin/target/vcell-admin-0.0.1-SNAPSHOT.jar \ diff --git a/docker/build/Dockerfile-api-dev b/docker/build/Dockerfile-api-dev index 0c95ce306c..0e4d06cd6a 100644 --- a/docker/build/Dockerfile-api-dev +++ b/docker/build/Dockerfile-api-dev @@ -3,15 +3,17 @@ FROM ubuntu:18.04 RUN apt-get -y update && \ apt-get install -y curl openjdk-8-jre dnsutils && \ mkdir -p /usr/local/app/lib - + +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime + WORKDIR /usr/local/app COPY ./vcell-server/target/vcell-server-0.0.1-SNAPSHOT.jar \ ./vcell-server/target/maven-jars/*.jar \ ./vcell-oracle/target/vcell-oracle-0.0.1-SNAPSHOT.jar \ ./vcell-oracle/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./vcell-api/target/vcell-api-0.0.1-SNAPSHOT.jar \ ./vcell-api/target/maven-jars/*.jar \ ./lib/ @@ -62,6 +64,7 @@ ENTRYPOINT java \ -Dvcell.server.dbDriverName="${dbdriver}" \ -Dvcell.server.dbUserid="${dbuser}" \ -Dvcell.db.pswdfile="${dbpswdfile}" \ + -Duser.timezone="${userTimezone}" \ -Dvcell.jms.int.host.internal="${jmshost_int_internal}" \ -Dvcell.jms.int.port.internal="${jmsport_int_internal}" \ -Dvcell.jms.blobMessageUseMongo=true \ diff --git a/docker/build/Dockerfile-batch-dev b/docker/build/Dockerfile-batch-dev index f6871e5832..9e3ba3fc76 100644 --- a/docker/build/Dockerfile-batch-dev +++ b/docker/build/Dockerfile-batch-dev @@ -86,6 +86,10 @@ RUN apt-get -y update && \ # wget --no-check-certificate https://download.opensuse.org/repositories/home:/fbergman:/COPASI/xUbuntu_17.10/amd64/python-copasi_4.22.170-1_amd64.deb && \ # gdebi -n -q python-copasi_4.22.170-1_amd64.deb +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime + RUN mkdir -p /usr/local/app/localsolvers && ln -s /vcellbin /usr/local/app/localsolvers/linux64 WORKDIR /usr/local/app diff --git a/docker/build/Dockerfile-data-dev b/docker/build/Dockerfile-data-dev index 52f6f48510..75709484b3 100644 --- a/docker/build/Dockerfile-data-dev +++ b/docker/build/Dockerfile-data-dev @@ -58,7 +58,11 @@ RUN mkdir -p /usr/local/app && \ apk update && \ apk add --no-cache ttf-dejavu && \ apk add openssh-client && \ - apk add screen + apk add screen tzdata + +ENV TZ=America/New_York +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime WORKDIR /usr/local/app @@ -66,8 +70,6 @@ COPY ./vcell-server/target/vcell-server-0.0.1-SNAPSHOT.jar \ ./vcell-server/target/maven-jars/*.jar \ ./vcell-oracle/target/vcell-oracle-0.0.1-SNAPSHOT.jar \ ./vcell-oracle/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./lib/ diff --git a/docker/build/Dockerfile-db-dev b/docker/build/Dockerfile-db-dev index 8f14ebc113..fd905deb9f 100644 --- a/docker/build/Dockerfile-db-dev +++ b/docker/build/Dockerfile-db-dev @@ -2,7 +2,11 @@ FROM openjdk:8u151-jdk-alpine3.7 RUN mkdir -p /usr/local/app && \ apk update && \ - apk add screen + apk add screen tzdata + +ENV TZ=America/New_York +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime WORKDIR /usr/local/app @@ -10,8 +14,6 @@ COPY ./vcell-server/target/vcell-server-0.0.1-SNAPSHOT.jar \ ./vcell-server/target/maven-jars/*.jar \ ./vcell-oracle/target/vcell-oracle-0.0.1-SNAPSHOT.jar \ ./vcell-oracle/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./lib/ diff --git a/docker/build/Dockerfile-sched-dev b/docker/build/Dockerfile-sched-dev index 0451c7ca77..1ae57b62d6 100644 --- a/docker/build/Dockerfile-sched-dev +++ b/docker/build/Dockerfile-sched-dev @@ -4,7 +4,11 @@ RUN mkdir -p /usr/local/app && \ apk update && \ apk add openssh-client && \ apk add curl && \ - apk add screen + apk add screen tzdata + +ENV TZ=America/New_York +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime WORKDIR /usr/local/app @@ -12,8 +16,6 @@ COPY ./vcell-server/target/vcell-server-0.0.1-SNAPSHOT.jar \ ./vcell-server/target/maven-jars/*.jar \ ./vcell-oracle/target/vcell-oracle-0.0.1-SNAPSHOT.jar \ ./vcell-oracle/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./lib/ diff --git a/docker/build/Dockerfile-submit-dev b/docker/build/Dockerfile-submit-dev index 44b1e5953d..0c4567d536 100644 --- a/docker/build/Dockerfile-submit-dev +++ b/docker/build/Dockerfile-submit-dev @@ -3,7 +3,11 @@ FROM openjdk:8u151-jdk-alpine3.7 RUN mkdir -p /usr/local/app && \ apk update && \ apk add openssh-client && \ - apk add screen + apk add screen tzdata + +ENV TZ=America/New_York +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime WORKDIR /usr/local/app diff --git a/docker/build/Dockerfile-web-dev b/docker/build/Dockerfile-web-dev index fc94afbaac..f9da0dac6a 100644 --- a/docker/build/Dockerfile-web-dev +++ b/docker/build/Dockerfile-web-dev @@ -4,14 +4,16 @@ RUN apt-get -y update && \ apt-get install -y curl openjdk-8-jre wget dnsutils && \ mkdir -p /usr/local/app/lib +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata +RUN unlink /etc/localtime || true +RUN ln -s /usr/share/zoneinfo/America/New_York /etc/localtime + WORKDIR /usr/local/app COPY ./vcell-web/target/vcell-web-0.0.1-SNAPSHOT.jar \ ./vcell-web/target/maven-jars/*.jar \ ./vcell-oracle/target/vcell-oracle-0.0.1-SNAPSHOT.jar \ ./vcell-oracle/target/maven-jars/*.jar \ - ./non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar \ - ./non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar \ ./lib/ diff --git a/docker/database/backup_scripts/.gitignore b/docker/database/backup_scripts/.gitignore new file mode 100644 index 0000000000..d43b332b63 --- /dev/null +++ b/docker/database/backup_scripts/.gitignore @@ -0,0 +1 @@ +password.txt \ No newline at end of file diff --git a/docker/database/backup_scripts/README.md b/docker/database/backup_scripts/README.md new file mode 100644 index 0000000000..71acc21b0b --- /dev/null +++ b/docker/database/backup_scripts/README.md @@ -0,0 +1,15 @@ +## Nightly Oracle database backups + +### Data Pump dump to /data/backup +cron job runs `archivedb.sh` every 1:00am as `root` + +``` +0 1 * * * /data/backup/scripts/archivedb.sh >> /data/backup/backup.cron-log 2>&1 +``` + +### Compress and Archive to ~vcell/database_backups +cron job runs `move_files_to_vcellhome.sh` every 9:00pm as `vcell` + +``` +0 21 * * * /data/backup/scripts/move_files_to_vcellhome.sh >> /data/backup/move_files.cron-log 2>&1 +``` diff --git a/docker/database/backup_scripts/archivedb.sh b/docker/database/backup_scripts/archivedb.sh new file mode 100644 index 0000000000..5a5a8fde6a --- /dev/null +++ b/docker/database/backup_scripts/archivedb.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# run export of oracle 19c database running in the container named 'oracle-database' using the Data Pump 'expdp' command +sudo docker exec oracle-database /opt/oracle/backup/scripts/run.sh 2>&1 | tee -a /data/backup/archivedb_$(date +%Y_%m_%d_%H_%M_%S).log + +# change permission so that vcell can move them in a separate script and cron job +chmod 666 /data/backup/*.log +chmod 666 /data/backup/*.dmp + +# copy move dump file to remote storage +echo "vcell runs separate script to archive to ~/vcell/database_backups" \ No newline at end of file diff --git a/docker/database/backup_scripts/move_files_to_vcellhome.sh b/docker/database/backup_scripts/move_files_to_vcellhome.sh new file mode 100644 index 0000000000..4a1bd430d0 --- /dev/null +++ b/docker/database/backup_scripts/move_files_to_vcellhome.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo "" +echo "========= starting move of database backups at $(date) ========" + +cd /data/backup || echo "failed to cd to /data/backup" || exit 1 + +echo "" +echo "---- before compressing/moving files ----" +ls -oth +echo "-----------------------------------------" +echo "" + +echo "compressing all .dmp files in /data/backup" +gzip ./*.dmp + +# move +echo "moving all .log and .dmp.gz files to ~/vcell/database_backups" +mv /data/backup/*.log ~vcell/database_backups +mv /data/backup/*.dmp.gz ~vcell/database_backups + +echo "" +echo "---- after compressing/moving files ----" +ls -oth +echo "-----------------------------------------" +echo "" + +echo "========== done moving files $(date) ========================" +echo "" diff --git a/docker/database/backup_scripts/newscn.sql b/docker/database/backup_scripts/newscn.sql new file mode 100644 index 0000000000..5b9e89163a --- /dev/null +++ b/docker/database/backup_scripts/newscn.sql @@ -0,0 +1,3 @@ +set heading off +Select CURRENT_SCN from v$database; +quit diff --git a/docker/database/backup_scripts/run.sh b/docker/database/backup_scripts/run.sh new file mode 100644 index 0000000000..1a6fea334d --- /dev/null +++ b/docker/database/backup_scripts/run.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# export the oracle 19c database running in the container named 'oracle-database' using the Data Pump 'expdp' command +# the export is written to the directory /data/backup on the database host with a timestamped .dmp file and .log file +# +# this script (archive.sh) is to be placed in /data/backup/scripts on the database host, along with newscn.sql and dbpswd.txt +# usage +# sudo docker exec -it oracle-database /opt/oracle/backup/scripts/run.sh +# or +# sudo docker exec -it oracle-database /opt/oracle/backup/scripts/run.sh 2>&1 | tee -a archivedb_$(date +%Y_%m_%d_%H_%M).log +# + +cd /opt/oracle/backup/scripts || echo "failed to cd to /opt/oracle/backup/scripts" || exit 1 + +dateString=$(date +%Y_%m_%d_%H_%M_%S) +logfile=orclpdb1_${dateString}.log +dumpfile=orclpdb1_${dateString}.dmp + +password=$(cat /opt/oracle/backup/scripts/password.txt) + +# data the database SCN (system change number) at the time of the export +SCN=$(sqlplus -S -L "system/${password}@ORCLPDB1" @newscn.sql | xargs) || echo "error getting scn" || exit 1 +echo SCN is "'${SCN}'" + +# dump to EXT_DATA_PUMP_DIR /opt/oracle/backup mounted to host /data/backup with timestamped .dmp and .log files +expdp \ + "system/${password}@localhost:1521/ORCLPDB1" \ + directory=EXT_DATA_PUMP_DIR \ + dumpfile="${dumpfile}" \ + logfile="${logfile}" \ + schemas=vcell \ + flashback_scn="${SCN}" + +expdp_returnCode=$? +if [ $expdp_returnCode != 0 ]; then + echo "Error exporting database" + exit 1 +else + echo "database dump to ${dumpfile} complete, please check logs in ${logfile}" + exit 0 +fi diff --git a/docker/kubernetes/vcelldb-uchc.yaml b/docker/kubernetes/vcelldb-uchc.yaml index 653fb6b6c3..ebe4ba75fb 100644 --- a/docker/kubernetes/vcelldb-uchc.yaml +++ b/docker/kubernetes/vcelldb-uchc.yaml @@ -5,4 +5,4 @@ metadata: namespace: prod spec: type: ExternalName - externalName: vcell-db.cam.uchc.edu + externalName: vcell-oracle.cam.uchc.edu diff --git a/docker/swarm/README_RebuildSwarm.md b/docker/swarm/README_RebuildSwarm.md new file mode 100644 index 0000000000..671600e728 --- /dev/null +++ b/docker/swarm/README_RebuildSwarm.md @@ -0,0 +1,43 @@ +# DESTROY and RECREATE Docker Swarm (alpha site example) + +## destroy current swarm +#### shutdown current stack (run on vcellapi-beta) +```bash +sudo docker stack rm vcellalpha +``` +#### log into each node and leave the current swarm (run on vcellapi-beta,vcell-node3,vcell-node4) +```bash +sudo docker swarm leave --force +``` +## create new swarm and add nodes +### create new swarm, current node joins cluster (run on vcellapi-beta) +```bash +sudo docker swarm init +sudo docker swarm join-token manager +sudo docker swarm join-token worker +``` +### join other 2 nodes as managers +using **manager** "join token" from above (run on vcell-node3 and vcell-node4). +All 3 nodes are added as managers (must be odd number). +This way can shutdown one node at a time and not loose quorum. +```bash +sudo docker swarm join --token SWMTKN-1-... 155.37.255.68:2377 +``` +### confirm that there are 3 nodes in this cluster (run on any node) +```bash +sudo docker node ls +ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION +o1p1u8b0f8ghzdrxevdnfaoqk vcell-node3.cam.uchc.edu Ready Active Reachable 20.10.10 +xd2k5pr8q6suoiqknexz31z68 * vcell-node4.cam.uchc.edu Ready Active Reachable 20.10.10 +6jw2v85djrf33b2ws9ax2a3q5 vcellapi-beta.cam.uchc.edu Ready Active Leader 20.10.22 +``` +### label INTERNAL nodes used to schedule stack services (run on vcell-node3,vcell-node4) +DMZ nodes (e.g. vcellapi,vcellapi-beta) cannot talk to some external resources (e.g. submit nodes) +```bash +sudo docker node update --label-add zone=INTERNAL {NODE_ID of created/joined node, * is current node} +``` +### confirm that nodes are labeled (run on any node) +```bash +sudo docker node inspect --pretty o1p1u8b0f8ghzdrxevdnfaoqk +sudo docker node inspect --pretty xd2k5pr8q6suoiqknexz31z68 +``` \ No newline at end of file diff --git a/docker/swarm/README_admin.md b/docker/swarm/README_admin.md index c182c546c8..e06555b544 100644 --- a/docker/swarm/README_admin.md +++ b/docker/swarm/README_admin.md @@ -1,3 +1,5 @@ +## Restart sites + ### Restart Release Site (from production swarm cluster, e.g. vcellapi.cam.uchc.edu or vcell-node1 or vcell-node2) sudo docker service update --force --detach=false vcellrel_activemqint @@ -8,7 +10,6 @@ sudo docker service update --force --detach=false vcellrel_data sudo docker service update --force --detach=false vcellrel_sched sudo docker service update --force --detach=false vcellrel_submit sudo docker service update --force --detach=false vcellrel_api -(not used) sudo docker service update --force --detach=false vcellrel_opt ### Restart Alpha Site (from development swarm cluster, e.g. vcellapi-beta.cam.uchc.edu or vcell-node3 or vcell-node4) @@ -19,5 +20,76 @@ sudo docker service update --force --detach=false vcellalpha_db sudo docker service update --force --detach=false vcellalpha_data sudo docker service update --force --detach=false vcellalpha_sched sudo docker service update --force --detach=false vcellalpha_submit -sudo docker service update --force --detach=false vcellalpha_api -(not used) sudo docker service update --force --detach=false vcellalpha_opt +sudo docker service update --force --detach=false vcellalpha_api + + +## Simulation Message Queue administration +see also: [serverconfig-uch.sh](serverconfig-uch.sh) for current definition of VCELL_JMS_SIM_RESTPORT_EXTERNAL. + +### Release site activemq console (not exposed to the public internet) +```bash +_site_port_offset=0 # 0 for production, 1 for beta, 2 for alpha, 3 for test +VCELL_JMS_SIM_RESTPORT_EXTERNAL=$((8161 + _site_port_offset)) +open http://vcellapi.cam.uchc.edu:${VCELL_JMS_SIM_RESTPORT_EXTERNAL}/admin/queues.jsp +# login with admin/admin +``` + +### Alpha site activemq console (not exposed to the public internet) +```bash +_site_port_offset=2 # 0 for production, 1 for beta, 2 for alpha, 3 for test +VCELL_JMS_SIM_RESTPORT_EXTERNAL=$((8161 + _site_port_offset)) +open http://vcellapi-beta.cam.uchc.edu:${VCELL_JMS_SIM_RESTPORT_EXTERNAL}/admin/queues.jsp +# login with admin/admin +``` + +## cron job to restart scheduler every 6 hours +see vcellapi.cam.uchc.edu:/root/vcell_cron_service_update.sh + +## force alpha site to replicas=0 +```bash +sudo docker service update --force --detach=false --replicas=0 vcellalpha_activemqint +sudo docker service update --force --detach=false --replicas=0 vcellalpha_activemqsim +sudo docker service update --force --detach=false --replicas=0 vcellalpha_mongodb +sudo docker service update --force --detach=false --replicas=0 vcellalpha_db +sudo docker service update --force --detach=false --replicas=0 vcellalpha_data +sudo docker service update --force --detach=false --replicas=0 vcellalpha_sched +sudo docker service update --force --detach=false --replicas=0 vcellalpha_submit +sudo docker service update --force --detach=false --replicas=0 vcellalpha_api +``` + +## force alpha site to replicas=1 +```bash +sudo docker service update --force --detach=false --replicas=1 vcellalpha_activemqint +sudo docker service update --force --detach=false --replicas=1 vcellalpha_activemqsim +sudo docker service update --force --detach=false --replicas=1 vcellalpha_mongodb +sudo docker service update --force --detach=false --replicas=1 vcellalpha_db +sudo docker service update --force --detach=false --replicas=1 vcellalpha_data +sudo docker service update --force --detach=false --replicas=1 vcellalpha_sched +sudo docker service update --force --detach=false --replicas=1 vcellalpha_submit +sudo docker service update --force --detach=false --replicas=1 vcellalpha_api +``` + +## force release site to replicas=0 +```bash +sudo docker service update --force --detach=false --replicas=0 vcellrel_activemqint +sudo docker service update --force --detach=false --replicas=0 vcellrel_activemqsim +sudo docker service update --force --detach=false --replicas=0 vcellrel_mongodb +sudo docker service update --force --detach=false --replicas=0 vcellrel_db +sudo docker service update --force --detach=false --replicas=0 vcellrel_data +sudo docker service update --force --detach=false --replicas=0 vcellrel_sched +sudo docker service update --force --detach=false --replicas=0 vcellrel_submit +sudo docker service update --force --detach=false --replicas=0 vcellrel_api +``` + +## force release site to replicas=1 +```bash +sudo docker service update --force --detach=false --replicas=1 vcellrel_activemqint +sudo docker service update --force --detach=false --replicas=1 vcellrel_activemqsim +sudo docker service update --force --detach=false --replicas=1 vcellrel_mongodb +sudo docker service update --force --detach=false --replicas=1 vcellrel_db +sudo docker service update --force --detach=false --replicas=1 vcellrel_data +sudo docker service update --force --detach=false --replicas=1 vcellrel_sched +sudo docker service update --force --detach=false --replicas=1 vcellrel_submit +sudo docker service update --force --detach=false --replicas=1 vcellrel_api +``` + diff --git a/docker/swarm/localconfig_realslurm_oracle.sh b/docker/swarm/localconfig_realslurm_oracle.sh index 100174e1e8..150cac993a 100755 --- a/docker/swarm/localconfig_realslurm_oracle.sh +++ b/docker/swarm/localconfig_realslurm_oracle.sh @@ -76,7 +76,7 @@ case $VCELL_SITE in ;; esac -VCELL_DB_URL="jdbc:oracle:thin:@VCELL-DB.cam.uchc.edu:1521/vcelldborcl.cam.uchc.edu" +VCELL_DB_URL="jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521/ORCLPDB1" VCELL_DB_DRIVER="oracle.jdbc.driver.OracleDriver" VCELL_DB_USER="vcell" VCELL_DEBUG_PORT_BASE=5000 diff --git a/docker/swarm/serverconfig-uch.sh b/docker/swarm/serverconfig-uch.sh index 629ba665bc..3a96975aa2 100755 --- a/docker/swarm/serverconfig-uch.sh +++ b/docker/swarm/serverconfig-uch.sh @@ -98,7 +98,7 @@ case $VCELL_SITE in ;; esac -VCELL_DB_URL="jdbc:oracle:thin:@VCELL-DB.cam.uchc.edu:1521/vcelldborcl.cam.uchc.edu" +VCELL_DB_URL="jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521/ORCLPDB1" VCELL_DB_DRIVER="oracle.jdbc.driver.OracleDriver" VCELL_DB_USER="vcell" VCELL_DEBUG_PORT_BASE=5000 diff --git a/docker_run.sh b/docker_run.sh index 7dc68ab042..45182f7729 100755 --- a/docker_run.sh +++ b/docker_run.sh @@ -90,6 +90,6 @@ java \ -Dvcell.mongodb.host.internal="localhost" \ -Dvcell.mongodb.port.internal=27017 \ -Dvcell.server.dbDriverName=oracle.jdbc.driver.OracleDriver \ - -Dvcell.server.dbConnectURL=jdbc:oracle:thin:@VCELL-DB.cam.uchc.edu:1521/vcelldborcl.cam.uchc.edu \ + -Dvcell.server.dbConnectURL=jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521/ORCLPDB1 \ -Dcli.workingDir=/usr/local/app/vcell/installDir/python/vcell_cli_utils/ $(if [ $MAX_JAVA_MEM_MB -gt 0 ]; then echo "-Xmx${MAX_JAVA_MEM_MB}m"; fi) \ org.vcell.cli.CLIStandalone $command $arguments diff --git a/localsolvers/win64/.gitignore b/localsolvers/win64/.gitignore index e85205eba2..8f112256fb 100644 --- a/localsolvers/win64/.gitignore +++ b/localsolvers/win64/.gitignore @@ -16,3 +16,4 @@ /testzip.exe /ziptool.exe /zlib1.dll +/LangevinNoVis_x64.exe.install4j diff --git a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar b/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar deleted file mode 100644 index 767eba7f8f..0000000000 Binary files a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar and /dev/null differ diff --git a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar.md5 b/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar.md5 deleted file mode 100644 index aab7f8b7b8..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -76852c42c44401f44d26319a74e55f5b \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar.sha1 b/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar.sha1 deleted file mode 100644 index e06429ac79..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a483a046eee2f404d864a6ff5b09dc0e1be3fe6c \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom b/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom deleted file mode 100644 index 23f06336c4..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom +++ /dev/null @@ -1,8 +0,0 @@ - - - 4.0.0 - com.oracle - ojdbc6 - 11.2.0.4 - diff --git a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom.md5 b/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom.md5 deleted file mode 100644 index 550c8a39b8..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -396995033448cedbf533593a5ea880b8 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom.sha1 b/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom.sha1 deleted file mode 100644 index 953f9509ec..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/11.2.0.4/ojdbc6-11.2.0.4.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -ae3a8f66e391c559949956d0db1dbce11f4152f1 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml b/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml deleted file mode 100644 index 3f5231d260..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - com.oracle - ojdbc6 - - 11.2.0.4 - - 11.2.0.4 - - 20200617021818 - - diff --git a/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml.md5 b/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml.md5 deleted file mode 100644 index f8e8195ca4..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml.md5 +++ /dev/null @@ -1 +0,0 @@ -4c6b7b1d68a3937ff6404afeb9b9e057 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml.sha1 b/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml.sha1 deleted file mode 100644 index 34ff7d4cfd..0000000000 --- a/non-maven-java-libs/com/oracle/ojdbc6/maven-metadata.xml.sha1 +++ /dev/null @@ -1 +0,0 @@ -d5b811bf96b1994f8fcd714f4acfd737a5678596 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar b/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar deleted file mode 100644 index 45c4b46096..0000000000 Binary files a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar and /dev/null differ diff --git a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar.md5 b/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar.md5 deleted file mode 100644 index 4b557ca02f..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -4c0bbd1748cd5794a5d27800a0cdb558 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar.sha1 b/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar.sha1 deleted file mode 100644 index 6b0ddeb1e5..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -5520b4e492939b477cc9ced90c03bc72710dcaf3 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom b/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom deleted file mode 100644 index 6f8db55e9a..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom +++ /dev/null @@ -1,8 +0,0 @@ - - - 4.0.0 - com.oracle - ucp - 11.2.0.4 - diff --git a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom.md5 b/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom.md5 deleted file mode 100644 index 376ba0e875..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -b3f0528b9d406c416e223a4a8fdb98e0 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom.sha1 b/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom.sha1 deleted file mode 100644 index 4f1588e51a..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/11.2.0.4/ucp-11.2.0.4.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -3d6de1a311f1eecd8475b72f8af97f71b2b4ab51 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml b/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml deleted file mode 100644 index b478d35235..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - com.oracle - ucp - - 11.2.0.4 - - 11.2.0.4 - - 20200617021951 - - diff --git a/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml.md5 b/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml.md5 deleted file mode 100644 index 3f5c66928d..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml.md5 +++ /dev/null @@ -1 +0,0 @@ -05ed95ab7eec05382756b23ed873bcb8 \ No newline at end of file diff --git a/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml.sha1 b/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml.sha1 deleted file mode 100644 index 8832295481..0000000000 --- a/non-maven-java-libs/com/oracle/ucp/maven-metadata.xml.sha1 +++ /dev/null @@ -1 +0,0 @@ -f4ccf14df23055e5fed97e66a246b10da6534ee5 \ No newline at end of file diff --git a/pom.xml b/pom.xml index e4c0a17e3b..0931dd9dd5 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,7 @@ none + 19.20.0.0 diff --git a/vcell-admin/src/main/java/org/vcell/admin/ListservMail.java b/vcell-admin/src/main/java/org/vcell/admin/ListservMail.java index 9d455c8903..06700e7191 100644 --- a/vcell-admin/src/main/java/org/vcell/admin/ListservMail.java +++ b/vcell-admin/src/main/java/org/vcell/admin/ListservMail.java @@ -163,8 +163,8 @@ private static void getBouncedEmails(String emailPassword) throws Exception{ public static ArrayList queryEmails(String dbPassword) throws Exception{ String driverName = "oracle.jdbc.driver.OracleDriver"; - String host = "vcell-db.cam.uchc.edu"; - String db = "vcelldborcl"; + String host = "vcell-oracle.cam.uchc.edu"; + String db = "ORCLPDB1"; String connectURL = "jdbc:oracle:thin:@" + host + ":1521:" + db; ArrayList results = new ArrayList<>(); diff --git a/vcell-admin/src/test/java/org/vcell/support/EmailList.java b/vcell-admin/src/test/java/org/vcell/support/EmailList.java index b66f79a759..7e9b263fc6 100644 --- a/vcell-admin/src/test/java/org/vcell/support/EmailList.java +++ b/vcell-admin/src/test/java/org/vcell/support/EmailList.java @@ -27,7 +27,7 @@ public class EmailList { private static final String BOUNCE_QUERY = "Select email from vcell.mailbounce"; private static final String DEFAULT_FILE = "emails.csv"; - static final String JDBC_URL = "jdbc:oracle:thin:@vcell-db.cam.uchc.edu:1521/vcelldborcl.cam.uchc.edu"; + static final String JDBC_URL = "jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521/ORCLPDB1"; static final String USER_ID = "vcell"; private final String password; private final String filename; diff --git a/vcell-cli/src/main/java/org/vcell/cli/vcml/VCMLHandler.java b/vcell-cli/src/main/java/org/vcell/cli/vcml/VCMLHandler.java index 95aac2bd4d..b4f21e8421 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/vcml/VCMLHandler.java +++ b/vcell-cli/src/main/java/org/vcell/cli/vcml/VCMLHandler.java @@ -61,7 +61,7 @@ public static VCDocument convertVcmlToVcDocument(File vcmlFilePath) throws Excep if (astModel.hasCompartments()) { Structure struct = bioModel.getModel().getStructure(0); if (struct != null) { - bioModel.getModel().removeStructure(struct); + bioModel.getModel().removeStructure(struct, true); } } RbmUtils.BnglObjectConstructionVisitor constructionVisitor = null; diff --git a/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java b/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java index cf585802d0..755a9daee8 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java +++ b/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java @@ -3273,7 +3273,7 @@ private void openAfterChecking(VCDocumentInfo documentInfo, final TopLevelWindow if (astModel.hasCompartments()) { Structure struct = bioModel.getModel().getStructure(0); if (struct != null) { - bioModel.getModel().removeStructure(struct); + bioModel.getModel().removeStructure(struct, true); } } BnglObjectConstructionVisitor constructionVisitor = null; @@ -3437,7 +3437,7 @@ public void run(Hashtable hashTable) throws Exception { if (astModel.hasCompartments()) { Structure struct = bioModel.getModel().getStructure(0); if (struct != null) { - bioModel.getModel().removeStructure(struct); + bioModel.getModel().removeStructure(struct, true); } } diff --git a/vcell-client/src/main/java/cbit/vcell/client/ClientSimManager.java b/vcell-client/src/main/java/cbit/vcell/client/ClientSimManager.java index 23b8f81cb7..c9c65b54b6 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/ClientSimManager.java +++ b/vcell-client/src/main/java/cbit/vcell/client/ClientSimManager.java @@ -15,9 +15,11 @@ import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; @@ -31,10 +33,12 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Scanner; import java.util.Vector; import javax.swing.SwingUtilities; +import org.vcell.solver.langevin.LangevinSolver; import org.vcell.solver.smoldyn.SmoldynFileWriter; import org.vcell.solver.smoldyn.SmoldynSolver; import org.vcell.util.BeanUtils; @@ -495,7 +499,7 @@ public void run(Hashtable hashTable) throws Exception { File localSimDir = ResourceUtil.getLocalSimDir(User.tempUser.getName()); LocalVCDataIdentifier vcDataId = new LocalVCSimulationDataIdentifier(vcSimulationIdentifier, 0, localSimDir); DataManager dataManager = null; - if (sim.isSpatial()) { + if (!sim.getSolverTaskDescription().getSolverDescription().isLangevinSolver() && sim.isSpatial()) { dataManager = new PDEDataManager(outputContext,vcDataManager, vcDataId); } else { dataManager = new ODEDataManager(outputContext,vcDataManager, vcDataId); @@ -839,9 +843,13 @@ public void solverAborted(SolverEvent event) { }); solver.startSolver(); - while (true){ - try { - Thread.sleep(500); + int sleepInterval = 500; + if(solver instanceof LangevinSolver) { + sleepInterval = 2000; + } + while (true) { + try { + Thread.sleep(sleepInterval); } catch (InterruptedException e) { } @@ -854,6 +862,10 @@ public void solverAborted(SolverEvent event) { if (solverStatus.getStatus() == SolverStatus.SOLVER_ABORTED) { String simulationMessage = solverStatus.getSimulationMessage().getDisplayMessage(); String translatedMessage = solver.translateSimulationMessage(simulationMessage); + // uncomment the next 3 lines below to avoid a NPE when the message is null +// if(translatedMessage == null) { +// break; +// } if(translatedMessage.startsWith(BeanUtils.FD_EXP_MESSG)) { throw new RuntimeException("Sims with FieldData can only be run remotely (cannot use QuickRun).\n"+translatedMessage); }else { @@ -865,6 +877,58 @@ public void solverAborted(SolverEvent event) { solverStatus.getStatus() != SolverStatus.SOLVER_RUNNING){ break; } + if(solverStatus.getStatus() == SolverStatus.SOLVER_RUNNING) { + if(solver instanceof LangevinSolver) { + // + // TODO: it may (theoretically) be possible that a logfile exists from a previous run + // in which case there may be a brief interval where we display a Progress: 100% spurious result + // We should delete the log file for our run id early, right after initializing the solver + // in the call to createQuickRunSolver() above (or right after the call?) + // + getClientTaskStatusSupport().setMessage("Running... Progress: "); + LangevinSolver ls = (LangevinSolver)solver; + String logFileName = ls.getLogFileString(); + if(logFileName == null) { + getClientTaskStatusSupport().setProgress(0); + continue; + } + File logFile = new File(logFileName); + if(!logFile.exists()) { + getClientTaskStatusSupport().setProgress(0); + continue; + } + BufferedReader br = null; + FileReader fr = null; + String lastLine = null; + Scanner lineScanner; + int percentComplete = 0; + try { + fr = new FileReader(logFile); + br = new BufferedReader(fr); + Scanner sc = new Scanner(br); + // Get to the last line + while(sc.hasNextLine()){ + lastLine = sc.nextLine(); + } + sc.close(); + if(lastLine != null) { + if(!lastLine.startsWith("Simulation")) { + continue; + } + lineScanner = new Scanner(lastLine); + // Skip "Simulation" + lineScanner.next(); + String percent = lineScanner.next(); + percent = percent.substring(0,percent.length()-1); + percentComplete = Integer.parseInt(percent); + getClientTaskStatusSupport().setProgress(percentComplete); + lineScanner.close(); + } + } catch(FileNotFoundException fne) { + fne.printStackTrace(System.out); + } + } + } } } diff --git a/vcell-client/src/main/java/cbit/vcell/client/ClientTaskManager.java b/vcell-client/src/main/java/cbit/vcell/client/ClientTaskManager.java index 81846764fc..c40190e17e 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/ClientTaskManager.java +++ b/vcell-client/src/main/java/cbit/vcell/client/ClientTaskManager.java @@ -37,7 +37,7 @@ public class ClientTaskManager { public static AsynchClientTask[] newApplication(JComponent tempRequester, final BioModel bioModel, final SimulationContext.Application simContextType) { - AsynchClientTask task0 = new AsynchClientTask("create application", AsynchClientTask.TASKTYPE_SWING_BLOCKING) { + AsynchClientTask task0 = new AsynchClientTask("create application", AsynchClientTask.TASKTYPE_NONSWING_BLOCKING) { @Override public void run(Hashtable hashTable) throws Exception { diff --git a/vcell-client/src/main/java/cbit/vcell/client/data/ODEDataInterfaceImpl.java b/vcell-client/src/main/java/cbit/vcell/client/data/ODEDataInterfaceImpl.java index c67e1fe338..112ac42a43 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/data/ODEDataInterfaceImpl.java +++ b/vcell-client/src/main/java/cbit/vcell/client/data/ODEDataInterfaceImpl.java @@ -249,11 +249,18 @@ public ColumnDescription[] getFilteredColumnDescriptions() { if (dataSymbolMetadata!=null){ selectedFilterCategory = dataSymbolMetadata.filterCategory; } + for (int j = 0; j < selectedFilters.length; j++) { if(selectedFilters[j].equals(selectedFilterCategory)){ selectedColumnDescriptions.add(getOdeSolverResultSet().getColumnDescriptions()[i]); break; } + // Langevin values just shown as species + if(selectedFilters[j].getName().equals("Species") && selectedFilterCategory == null) { + selectedColumnDescriptions.add(getOdeSolverResultSet().getColumnDescriptions()[i]); + break; + } + } } return selectedColumnDescriptions.toArray(new ColumnDescription[0]); diff --git a/vcell-client/src/main/java/cbit/vcell/client/data/SimResultsViewer.java b/vcell-client/src/main/java/cbit/vcell/client/data/SimResultsViewer.java index 3cc63843d1..f1cc8dbae3 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/data/SimResultsViewer.java +++ b/vcell-client/src/main/java/cbit/vcell/client/data/SimResultsViewer.java @@ -96,7 +96,12 @@ public class SimResultsViewer extends DataViewer { public SimResultsViewer(Simulation simulation, DataManager arg_dataManager) throws DataAccessException { super(); setSimulation(simulation); - this.isODEData = !simulation.isSpatial(); + // If Langevin, ignore spatial so that it can be displayed as an ODE model + if (simulation.getSolverTaskDescription().getSolverDescription().isLangevinSolver()) { + this.isODEData = true; + } else { + this.isODEData = !simulation.isSpatial(); + } this.dataManager = arg_dataManager; initialize(); } diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/VCDocumentDecorator.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/VCDocumentDecorator.java index a405a3a32b..0ac1e85afa 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/VCDocumentDecorator.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/VCDocumentDecorator.java @@ -119,6 +119,11 @@ private List buildRequiredFeatures(SimulationContext sc) { case RULE_BASED_STOCHASTIC: sfList.add(SolverFeature.Feature_Rulebased); return sfList; + case SPRINGSALAD: + sfList.add(SolverFeature.Feature_Springs); + sfList.add(SolverFeature.Feature_Rulebased); + sfList.add(SolverFeature.Feature_Stochastic); + return sfList; // Feature_Spatial is already there case NETWORK_STOCHASTIC: //more analysis to determine whether hybrid or (pure) stochastic } @@ -158,9 +163,14 @@ private void checkRequired(IssueContext issueContext, List issueList,Simu Set missingFeatures = new HashSet<>(requiredFeatures); missingFeatures.removeAll(supportedFeatures); + boolean first = true; String text = "Solver " + solvDesc.getDatabaseName() + " does not support the following required features: \n"; for (SolverFeature sf : missingFeatures) { - text += sf.getName() + "\n"; + if(!first) { + text += ", "; + } + first = false; + text += "\n" + sf.getName(); } if (!missingFeatures.isEmpty()) { diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ApplicationSpecificationsPanel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ApplicationSpecificationsPanel.java index 8876bf6056..feb730c760 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ApplicationSpecificationsPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ApplicationSpecificationsPanel.java @@ -28,6 +28,7 @@ import org.vcell.model.rbm.gui.NetworkConstraintsPanel; import org.vcell.model.rbm.gui.NetworkFreePanel; +import org.vcell.model.springsalad.gui.MolecularStructuresPanel; import cbit.vcell.client.constants.GuiConstants; import cbit.vcell.client.desktop.biomodel.DocumentEditorTreeModel.DocumentEditorTreeFolderClass; @@ -54,6 +55,7 @@ public static interface Specifier { */ private NetworkConstraintsPanel networkConstraintsPanel; private NetworkFreePanel networkFreePanel; + private MolecularStructuresPanel molecularStructuresPanel; //private MembraneConditionsPanel membraneConditionsPanel; private JTextField textField_1; private static class SpecifierComponent { @@ -158,11 +160,13 @@ protected void initialize(){ networkConstraintsPanel = new NetworkConstraintsPanel(); networkFreePanel = new NetworkFreePanel(); MembraneConditionsPanel membraneConditionsPanel = new MembraneConditionsPanel(); + molecularStructuresPanel = new MolecularStructuresPanel(); //order of calls determines display order setupTab("Species",initialConditionsPanel); setupTab("Reaction",modelProcessSpecsPanel); setupTab("Membrane",membraneConditionsPanel); + setupTab("Molecular Structures", molecularStructuresPanel); setupTab("Network",networkConstraintsPanel); setupTab("Network-Free",networkFreePanel); @@ -171,6 +175,7 @@ protected void initialize(){ if (System.getProperty("showMembrane") != null) { activate(membraneConditionsPanel); } + activate(molecularStructuresPanel); JPanel searchPanel = new JPanel(); GridBagLayout gbl_searchPanel = new GridBagLayout(); @@ -240,11 +245,19 @@ public void setSimulationContext(SimulationContext newValue) { spc.setter.setSimulationContext(newValue); } if(simulationContext.getApplicationType().equals(SimulationContext.Application.RULE_BASED_STOCHASTIC)) { + deactivate(molecularStructuresPanel); deactivate(networkConstraintsPanel); activate(networkFreePanel); final int indexOfNetworkFreeTab = tabbedPane.indexOfComponent(networkFreePanel); tabbedPane.setEnabledAt(indexOfNetworkFreeTab, true); + } else if(simulationContext.getApplicationType().equals(SimulationContext.Application.SPRINGSALAD)) { + deactivate(networkFreePanel); + deactivate(networkConstraintsPanel); + activate(molecularStructuresPanel); + final int indexOfMolecularStructuresTab = tabbedPane.indexOfComponent(molecularStructuresPanel); + tabbedPane.setEnabledAt(indexOfMolecularStructuresTab, true); } else { // this panel only for flattened rule based applications + deactivate(molecularStructuresPanel); deactivate(networkFreePanel); activate(networkConstraintsPanel); @@ -257,6 +270,13 @@ public void setSimulationContext(SimulationContext newValue) { tabbedPane.setEnabledAt(indexOfNetworkTab, true); } } + if(simulationContext.getApplicationType().equals(SimulationContext.Application.SPRINGSALAD)) { + activate(molecularStructuresPanel); + final int indexOfMolecularStructuresTab = tabbedPane.indexOfComponent(molecularStructuresPanel); + tabbedPane.setEnabledAt(indexOfMolecularStructuresTab, true); + } else { + deactivate(molecularStructuresPanel); + } } @Override diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditor.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditor.java index 789ecb0976..1eaa09851c 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditor.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditor.java @@ -254,6 +254,10 @@ public void run(Hashtable hashTable) throws Exception { newApplication(Application.RULE_BASED_STOCHASTIC); break; } + case add_new_app_springsalad: { + newApplication(Application.SPRINGSALAD); + break; + } case copyName: String name = bioModel.getName(); StringSelection data = new StringSelection(name); diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsPanel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsPanel.java index 0d8bac356b..19fa5ca952 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsPanel.java @@ -45,6 +45,7 @@ import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.MathMappingCallback; import cbit.vcell.mapping.SimulationContext.NetworkGenerationRequirements; +import cbit.vcell.resource.PropertyLoader; import cbit.vcell.solver.Simulation; import cbit.vcell.xml.XmlHelper; import cbit.vcell.xml.XmlParseException; @@ -76,6 +77,7 @@ public class BioModelEditorApplicationsPanel extends BioModelEditorRightSidePane private JMenuItem appNewStochApp = null; private JMenuItem appNewDeterministicApp = null; private JMenuItem appNewRulebasedApp = null; + private JMenuItem appNewSpringSaLaDApp = null; private JMenuItem ivjJMenuItemAppCopy = null; private EventHandler eventHandler = new EventHandler(); @@ -89,6 +91,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } else if (e.getSource() == appNewStochApp || e.getSource() == appNewDeterministicApp || e.getSource() == appNewRulebasedApp + || e.getSource() == appNewSpringSaLaDApp || e.getSource() == getJMenuItemAppCopy() || e.getSource() == menuItemAppNonSpatialCopyStochastic || e.getSource() == menuItemNonSpatialCopyDeterministic @@ -345,10 +348,18 @@ private javax.swing.JPopupMenu getNewAppPopupMenu() { appNewRulebasedApp.setActionCommand(GuiConstants.ACTIONCMD_CREATE_RULEBASED_APPLICATION); appNewRulebasedApp.addActionListener(eventHandler); + appNewSpringSaLaDApp = new javax.swing.JMenuItem(GuiConstants.MENU_TEXT_SPRINGSALAD_APPLICATION); + appNewSpringSaLaDApp.setActionCommand(GuiConstants.ACTIONCMD_CREATE_SPRINGSALAD_APPLICATION); + appNewSpringSaLaDApp.addActionListener(eventHandler); + //add menu items to menu appsPopupMenu.add(appNewDeterministicApp); appsPopupMenu.add(appNewStochApp); appsPopupMenu.add(appNewRulebasedApp); + String enableSpringSaLaD = PropertyLoader.getProperty(PropertyLoader.enableSpringSaLaD, "false"); + if("true".equals(enableSpringSaLaD)) { + appsPopupMenu.add(appNewSpringSaLaDApp); + } // user code begin {1} // user code end } catch (java.lang.Throwable ivjExc) { diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsTableModel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsTableModel.java index 5d217e7cb8..3a7104ba0e 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsTableModel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorApplicationsTableModel.java @@ -102,6 +102,9 @@ public Object getValueAt(int row, int column) { case RULE_BASED_STOCHASTIC:{ return "Agent-based model, "+spatialDescription+", stochastic (SSA)"; } + case SPRINGSALAD:{ + return "Agent-based spatial model, "+spatialDescription+", stochastic (Particles)"; + } default:{ throw new RuntimeException("math type description not yet implemented"); } diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorTreeCellRenderer.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorTreeCellRenderer.java index 019387fde4..8856859108 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorTreeCellRenderer.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelEditorTreeCellRenderer.java @@ -24,6 +24,7 @@ import cbit.vcell.client.desktop.biomodel.DocumentEditorTreeModel.DocumentEditorTreeFolderNode; import cbit.vcell.desktop.BioModelNode; import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.model.Model.RbmModelContainer; import cbit.vcell.xml.gui.MiriamTreeModel.LinkNode; @@ -98,6 +99,9 @@ public Component getTreeCellRendererComponent( icon = VCellIcons.appStoSpatialIcon; toolTipSuffix = "Stochastic / Spatial"; } + } else if(simContext.getApplicationType() == Application.SPRINGSALAD) { + icon = VCellIcons.appSpringSaLaDSpatialIcon; + toolTipSuffix = "SpringSaLaD / Spatial"; } else { // deterministic if(simContext.getGeometry().getDimension() == 0) { icon = VCellIcons.appDetNonspIcon; diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelParametersPanel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelParametersPanel.java index 252c7cfdf0..ed15ec857f 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelParametersPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/BioModelParametersPanel.java @@ -77,6 +77,7 @@ import cbit.vcell.mapping.RateRule; import cbit.vcell.mapping.SimulationContext; import cbit.vcell.mapping.SpeciesContextSpec; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.SimulationContextParameter; import cbit.vcell.mapping.gui.SpeciesContextSpecsTableModel; import cbit.vcell.model.EditableSymbolTableEntry; @@ -160,6 +161,8 @@ public Component getListCellRendererComponent(JList list, Obje } else { icon = VCellIcons.appStoSpatialIcon; } + } else if(simContext.getApplicationType() == Application.SPRINGSALAD) { + icon = VCellIcons.appSpringSaLaDSpatialIcon; } else { if(simContext.getGeometry().getDimension() == 0) { icon = VCellIcons.appDetNonspIcon; diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditor.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditor.java index b0494c8c1f..d9928b1fa6 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditor.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditor.java @@ -72,6 +72,7 @@ import cbit.vcell.desktop.MathModelMetaDataPanel; import cbit.vcell.mapping.SimulationContext; import cbit.vcell.mathmodel.MathModel; +import cbit.vcell.resource.PropertyLoader; import cbit.vcell.xml.gui.MiriamTreeModel.LinkNode; /** * Insert the type's description here. @@ -97,6 +98,7 @@ protected enum DocumentEditorPopupMenuAction { add_new_app_deterministic, add_new_app_stochastic, add_new_app_rulebased, + add_new_app_springsalad, copy_app, rename, delete, @@ -118,6 +120,7 @@ protected enum DocumentEditorPopupMenuAction { private JMenuItem addNewAppDeterministicMenuItem = null; private JMenuItem addNewAppStochasticMenuItem = null; private JMenuItem addNewAppRulebasedMenuItem = null; + private JMenuItem addNewAppSpringSaLaDMenuItem = null; private JMenuItem expandAllMenuItem = null; private JMenuItem collapseAllMenuItem = null; private JMenuItem addNewMenuItem; @@ -254,6 +257,8 @@ public void actionPerformed(java.awt.event.ActionEvent e) { popupMenuActionPerformed(DocumentEditorPopupMenuAction.add_new_app_stochastic, e.getActionCommand()); } else if (e.getSource() == addNewAppRulebasedMenuItem) { popupMenuActionPerformed(DocumentEditorPopupMenuAction.add_new_app_rulebased, e.getActionCommand()); + } else if (e.getSource() == addNewAppSpringSaLaDMenuItem) { + popupMenuActionPerformed(DocumentEditorPopupMenuAction.add_new_app_springsalad, e.getActionCommand()); } else if (e.getSource() == menuItemAppCopy || e.getSource() == menuItemNonSpatialCopyStochastic || e.getSource() == menuItemNonSpatialCopyDeterministic @@ -711,9 +716,15 @@ private void construcutPopupMenu() { addNewAppStochasticMenuItem.addActionListener(eventHandler); addNewAppRulebasedMenuItem = new JMenuItem(GuiConstants.MENU_TEXT_RULEBASED_APPLICATION); addNewAppRulebasedMenuItem.addActionListener(eventHandler); + addNewAppSpringSaLaDMenuItem = new JMenuItem(GuiConstants.MENU_TEXT_SPRINGSALAD_APPLICATION); + addNewAppSpringSaLaDMenuItem.addActionListener(eventHandler); addNewAppMenu.add(addNewAppDeterministicMenuItem); addNewAppMenu.add(addNewAppStochasticMenuItem); addNewAppMenu.add(addNewAppRulebasedMenuItem); + String enableSpringSaLaD = PropertyLoader.getProperty(PropertyLoader.enableSpringSaLaD, "false"); + if("true".equals(enableSpringSaLaD)) { + addNewAppMenu.add(addNewAppSpringSaLaDMenuItem); + } } popupMenu.add(addNewAppMenu); } diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditorTreeCellEditor.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditorTreeCellEditor.java index 6810dc1e0c..d08e67b567 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditorTreeCellEditor.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/DocumentEditorTreeCellEditor.java @@ -26,6 +26,7 @@ import cbit.vcell.desktop.BioModelNode; import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; public class DocumentEditorTreeCellEditor extends DefaultTreeCellEditor { @@ -71,6 +72,10 @@ public Component getTreeCellEditorComponent(JTree tree, Object value, renderer.setClosedIcon(VCellIcons.appStoSpatialIcon); renderer.setLeafIcon(VCellIcons.appStoSpatialIcon); } + } else if(sc.getApplicationType() == Application.SPRINGSALAD) { + renderer.setOpenIcon(VCellIcons.appSpringSaLaDSpatialIcon); + renderer.setClosedIcon(VCellIcons.appSpringSaLaDSpatialIcon); + renderer.setLeafIcon(VCellIcons.appSpringSaLaDSpatialIcon); } else { // deterministic if(sc.getGeometry().getDimension() == 0) { renderer.setOpenIcon(VCellIcons.appDetNonspIcon); diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssuePanel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssuePanel.java index 2b39cb9862..07c5c57ded 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssuePanel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssuePanel.java @@ -51,10 +51,14 @@ import cbit.vcell.mapping.GeometryContext; import cbit.vcell.mapping.GeometryContext.UnmappedGeometryClass; import cbit.vcell.mapping.MicroscopeMeasurement; +import cbit.vcell.mapping.MolecularInternalLinkSpec; import cbit.vcell.mapping.NetworkTransformer; import cbit.vcell.mapping.RateRule; +import cbit.vcell.mapping.ReactionRuleSpec; +import cbit.vcell.mapping.ReactionRuleSpec.ReactionRuleCombo; import cbit.vcell.mapping.ReactionSpec.ReactionCombo; import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SiteAttributesSpec; import cbit.vcell.mapping.SimulationContext.SimulationContextNameScope; import cbit.vcell.mapping.SpeciesContextSpec; import cbit.vcell.mapping.StructureMapping; @@ -280,11 +284,32 @@ else if (object instanceof Parameter) { followHyperlink(new ActiveView(null, DocumentEditorTreeFolderClass.REACTIONS_NODE, ActiveViewID.reactions), new Object[] {object}); } else if (object instanceof SpeciesContextSpec) { SpeciesContextSpec scs = (SpeciesContextSpec)object; - ActiveView av = new ActiveView(scs.getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.species_settings); + ActiveView av; + if(scs.getSimulationContext().getApplicationType() == SimulationContext.Application.SPRINGSALAD) { + av = new ActiveView(scs.getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.molecular_structure_setting); + } else { + av = new ActiveView(scs.getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.species_settings); + } followHyperlink(av, new Object[] {object}); +// } else if(object instanceof MolecularInternalLinkSpec) { +// MolecularInternalLinkSpec mils = (MolecularInternalLinkSpec)object; +// SpeciesContextSpec scs = mils.getSpeciesContextSpec(); +// ActiveView av = new ActiveView(scs.getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.molecular_structure_setting); +// followHyperlink(av, new Object[] {object}); +// } else if(object instanceof SiteAttributesSpec) { +// SiteAttributesSpec sas = (SiteAttributesSpec)object; +// SpeciesContextSpec scs = sas.getSpeciesContextSpec(); +// ActiveView av = new ActiveView(scs.getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.molecular_structure_setting); +// followHyperlink(av, new Object[] {object}); } else if (object instanceof ReactionCombo) { - ReactionCombo rc = (ReactionCombo)object; - followHyperlink(new ActiveView(rc.getReactionContext().getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.reaction_setting),new Object[] {((ReactionCombo)object).getReactionSpec()}); + ReactionCombo rCombo = (ReactionCombo)object; + followHyperlink(new ActiveView(rCombo.getReactionContext().getSimulationContext(), DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.reaction_setting),new Object[] {((ReactionCombo)object).getReactionSpec()}); + } else if (object instanceof ReactionRuleCombo) { // -------------------------- + ReactionRuleCombo rCombo = (ReactionRuleCombo)object; + SimulationContext simContext = rCombo.getReactionContext().getSimulationContext(); + ActiveView activeView = new ActiveView(simContext, DocumentEditorTreeFolderClass.SPECIFICATIONS_NODE, ActiveViewID.reaction_setting); + Object[] newSelection = new Object[] {((ReactionRuleCombo)object).getReactionSpec()}; + followHyperlink(activeView, newSelection); } else if (object instanceof SpeciesContext) { followHyperlink(new ActiveView(null, DocumentEditorTreeFolderClass.SPECIES_NODE, ActiveViewID.species), new Object[] {object}); } else if (object instanceof RbmObservable) { diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssueTableModel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssueTableModel.java index de96149727..f9c12e3950 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssueTableModel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/IssueTableModel.java @@ -40,10 +40,14 @@ import cbit.vcell.mapping.GeometryContext; import cbit.vcell.mapping.GeometryContext.UnmappedGeometryClass; import cbit.vcell.mapping.MicroscopeMeasurement; +import cbit.vcell.mapping.MolecularInternalLinkSpec; import cbit.vcell.mapping.RateRule; +import cbit.vcell.mapping.ReactionRuleSpec; +import cbit.vcell.mapping.ReactionRuleSpec.ReactionRuleCombo; import cbit.vcell.mapping.ReactionSpec; import cbit.vcell.mapping.ReactionSpec.ReactionCombo; import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SiteAttributesSpec; import cbit.vcell.mapping.SpeciesContextSpec; import cbit.vcell.mapping.StructureMapping; import cbit.vcell.mapping.StructureMapping.StructureMappingNameScope; @@ -286,6 +290,9 @@ private String getSourceObjectPathDescription(VCDocument vcDocument, Issue issue } else if (source instanceof ReactionCombo) { ReactionCombo rc = (ReactionCombo)source; description = "App(" + rc.getReactionContext().getSimulationContext().getName() + ") / Specifications / Reactions"; + } else if (source instanceof ReactionRuleCombo) { // -------------- + ReactionRuleCombo rrCombo = (ReactionRuleCombo)source; + description = "App(" + rrCombo.getReactionContext().getSimulationContext().getName() + ") / Specifications / Reactions"; } else if (source instanceof RbmModelContainer) { IssueCategory ic = issue.getCategory(); switch(ic) { @@ -322,6 +329,10 @@ private String getSourceObjectPathDescription(VCDocument vcDocument, Issue issue return "Protocols / Events"; } else if (source instanceof MathDescription) { return "Math Description"; + } else if(source instanceof SiteAttributesSpec) { + return "SpringSaLaD Application"; + } else if(source instanceof MolecularInternalLinkSpec) { + return "SpringSaLaD Application"; } else { System.err.println("unknown source type in IssueTableModel.getSourceObjectPathDescription(): " + source.getClass()); } @@ -411,8 +422,13 @@ private String getSourceObjectDescription(VCDocument vcDocument, Issue issue) { SpeciesContextSpec scs = (SpeciesContextSpec)object; description = scs.getSpeciesContext().getName(); } else if (object instanceof ReactionCombo) { - ReactionSpec rs = ((ReactionCombo)object).getReactionSpec(); + ReactionCombo rCombo = (ReactionCombo)object; + ReactionSpec rs = rCombo.getReactionSpec(); description = rs.getReactionStep().getName(); + } else if (object instanceof ReactionRuleCombo) { // -------------- + ReactionRuleCombo rrCombo = (ReactionRuleCombo)object; + ReactionRuleSpec rrs = rrCombo.getReactionSpec(); + description = rrs.getReactionRule().getName(); } else if (object instanceof RbmModelContainer) { //RbmModelContainer mc = (RbmModelContainer)object; description = "Rules validator"; @@ -430,6 +446,10 @@ private String getSourceObjectDescription(VCDocument vcDocument, Issue issue) { return ((AssignmentRule)object).getDisplayName()+""; } else if (object instanceof RateRule) { return ((RateRule)object).getDisplayName()+""; + } else if(object instanceof SiteAttributesSpec) { + description = ((SiteAttributesSpec)object).getDisplayName(); + } else if(object instanceof MolecularInternalLinkSpec) { + return "Link"; } else { System.err.println("unknown object type in IssueTableModel.getSourceObjectDescription(): " + object.getClass()); } diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypePropertiesPanel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypePropertiesPanel.java index e09fc5cff6..8cfada58d0 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypePropertiesPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypePropertiesPanel.java @@ -1152,6 +1152,17 @@ public void actionPerformed(ActionEvent e) { popupFromShapeMenu.add(deleteMenuItem); popupFromShapeMenu.add(new JSeparator()); popupFromShapeMenu.add(addMenuItem); +// // ---- start SpringSaLaD ----------------------------------------- +// JMenuItem setPositionMenuItem = new JMenuItem("Set Position "); +// popupFromShapeMenu.add(setPositionMenuItem); +// popupFromShapeMenu.add(new JSeparator()); +// JMenuItem addLinkMenuItem = new JMenuItem("Add Link "); +// JMenuItem removeLinkMenuItem = new JMenuItem("Remove Link "); +// JMenuItem setLinkLengthMenuItem = new JMenuItem("Set Link Length "); +// popupFromShapeMenu.add(addLinkMenuItem); +// popupFromShapeMenu.add(removeLinkMenuItem); +// popupFromShapeMenu.add(setLinkLengthMenuItem); +// // ---- end SpringSaLaD -------------------------------------------- deleteMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { MolecularComponent mc = (MolecularComponent) selectedObject; diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypeTableModel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypeTableModel.java index 60ba547405..4e524ee6ff 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypeTableModel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/MolecularTypeTableModel.java @@ -414,7 +414,11 @@ public void propertyChange(PropertyChangeEvent evt) { RbmUtils.addPropertyChangeListener(molecularType, this); } } - refreshData(); + if(!evt.getPropertyName().equals(MolecularType.PROPERTY_NAME_COMPONENT_LIST)) { + // this is a redirected message that serves a very narrow role, we don't want + // it to interfere with any previous functionality + refreshData(); + } // } else if (evt.getSource() == getModel().getRbmModelContainer().getNetworkConstraints()) { // if (evt.getPropertyName().equals(NetworkConstraints.PROPERTY_NAME_MAX_STOICHIOMETRY)) { // fireTableRowsUpdated(0, getRowCount() - 1); diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/SelectionManager.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/SelectionManager.java index 50fe33793c..a50ea4c610 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/SelectionManager.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/SelectionManager.java @@ -54,6 +54,7 @@ public static enum ActiveViewID { network_setting, network_free_setting, membrane_setting, + molecular_structure_setting, events, electrical, diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/VCellSortTableModel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/VCellSortTableModel.java index 16caefa193..a85a843735 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/VCellSortTableModel.java +++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/VCellSortTableModel.java @@ -31,6 +31,7 @@ import cbit.vcell.client.desktop.biomodel.IssueManager.IssueEvent; import cbit.vcell.client.desktop.biomodel.IssueManager.IssueEventListener; +import cbit.vcell.mapping.ReactionRuleSpec.ReactionRuleCombo; import cbit.vcell.mapping.ReactionSpec.ReactionCombo; import cbit.vcell.solver.OutputFunctionContext.OutputFunctionIssueSource; @@ -295,6 +296,10 @@ public List getIssues(int row, int col, Severity severity) { if(((ReactionCombo)source).getReactionSpec() == rowAt) { iL.add(issue); } + } else if(source instanceof ReactionRuleCombo) { + if(((ReactionRuleCombo)source).getReactionSpec() == rowAt) { + iL.add(issue); + } } else if (source instanceof OutputFunctionIssueSource) { if(((OutputFunctionIssueSource)source).getAnnotatedFunction() == rowAt) { iL.add(issue); diff --git a/vcell-client/src/main/java/cbit/vcell/client/task/ChooseFile.java b/vcell-client/src/main/java/cbit/vcell/client/task/ChooseFile.java index d059ae69c2..3e48f42b22 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/task/ChooseFile.java +++ b/vcell-client/src/main/java/cbit/vcell/client/task/ChooseFile.java @@ -39,6 +39,7 @@ import org.vcell.util.gui.exporter.FileFilters; import org.vcell.util.gui.exporter.SbmlExtensionFilter; import org.vcell.util.gui.exporter.SelectorExtensionFilter; +import org.vcell.util.gui.exporter.SpringSaLaDExtensionFilter; import org.vcell.util.gui.exporter.SelectorExtensionFilter.Selector; import cbit.vcell.biomodel.BioModel; @@ -223,6 +224,7 @@ private File showBioModelXMLFileChooser(Hashtable hashTable) thr */ fileChooser.addChoosableFileFilter(FileFilters.FILE_FILTER_SEDML); fileChooser.addChoosableFileFilter(FileFilters.FILE_FILTER_OMEX); + fileChooser.addChoosableFileFilter(FileFilters.FILE_FILTER_SPRINGSALAD); } else { defaultFileFilter = forceFileFilter; @@ -263,6 +265,15 @@ private File showBioModelXMLFileChooser(Hashtable hashTable) thr checkForOverwrites(selectedFile, topLevelWindowManager.getComponent(), userPreferences); // put the filter in the hash so the export task knows what to do... hashTable.put(FILE_FILTER, fileFilter); + if(fileFilter instanceof SpringSaLaDExtensionFilter && bioModel.getSimulationContexts().length > 0) { + if (fileFilter.requiresMoreChoices()) { + SimulationContext sc = bioModel.getSimulationContexts()[0]; + ExtensionFilter.ChooseContext ctx = new ExtensionFilter.ChooseContext(hashTable,topLevelWindowManager,currentWindow,sc,selectedFile,selectedFileName); + fileFilter.askUser(ctx); + } + resetPreferredFilePath(selectedFile, userPreferences); + return selectedFile; + } if (fileFilter.supports(SelectorExtensionFilter.Selector.FULL_MODEL)) { // nothing more to do in this case if (fileFilter.requiresMoreChoices()) { diff --git a/vcell-client/src/main/java/cbit/vcell/desktop/BioModelInfoCellRenderer.java b/vcell-client/src/main/java/cbit/vcell/desktop/BioModelInfoCellRenderer.java index cb5e193f8f..8b6f298b39 100644 --- a/vcell-client/src/main/java/cbit/vcell/desktop/BioModelInfoCellRenderer.java +++ b/vcell-client/src/main/java/cbit/vcell/desktop/BioModelInfoCellRenderer.java @@ -129,6 +129,8 @@ public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, } else { setIcon(VCellIcons.appStoSpatialIcon); } + } else if(MathType.SpringSaLaD.getDescription().equals(node.getRenderHint("appType"))) { + setIcon(VCellIcons.appSpringSaLaDSpatialIcon); } else if(MathType.Deterministic.getDescription().equals(node.getRenderHint("appType"))) { if("0".equals(node.getRenderHint("dimension"))) { setIcon(VCellIcons.appDetNonspIcon); diff --git a/vcell-client/src/main/java/cbit/vcell/geometry/gui/GeometryViewer.java b/vcell-client/src/main/java/cbit/vcell/geometry/gui/GeometryViewer.java index d771313316..33f8c91891 100644 --- a/vcell-client/src/main/java/cbit/vcell/geometry/gui/GeometryViewer.java +++ b/vcell-client/src/main/java/cbit/vcell/geometry/gui/GeometryViewer.java @@ -657,6 +657,7 @@ public void setGeometry(Geometry newValue) { break; case NETWORK_DETERMINISTIC: case NETWORK_STOCHASTIC: + case SPRINGSALAD: replaceEnabled = true; break; } diff --git a/vcell-client/src/main/java/cbit/vcell/graph/gui/ReactionCartoonTool.java b/vcell-client/src/main/java/cbit/vcell/graph/gui/ReactionCartoonTool.java index b662afde2d..2d8547952e 100644 --- a/vcell-client/src/main/java/cbit/vcell/graph/gui/ReactionCartoonTool.java +++ b/vcell-client/src/main/java/cbit/vcell/graph/gui/ReactionCartoonTool.java @@ -618,7 +618,7 @@ protected void menuAction(Shape shape, String menuAction) { } else if (menuAction.equals(CartoonToolEditActions.Delete.MENU_ACTION)) { try { if(getGraphModel().getSelectedShape() instanceof ReactionContainerShape && menuAction.equals(CartoonToolEditActions.Delete.MENU_ACTION)){ - getModel().removeStructure(((ReactionContainerShape)getGraphModel().getSelectedShape()).getStructure()); + getModel().removeStructure(((ReactionContainerShape)getGraphModel().getSelectedShape()).getStructure(), false); return; } if (getSelectedReactionStepArray()!=null || getSelectedSpeciesContextArray()!=null) { diff --git a/vcell-client/src/main/java/cbit/vcell/mapping/gui/ModelProcessSpecsTableModel.java b/vcell-client/src/main/java/cbit/vcell/mapping/gui/ModelProcessSpecsTableModel.java index 623117c3c4..c3dfaf4d28 100644 --- a/vcell-client/src/main/java/cbit/vcell/mapping/gui/ModelProcessSpecsTableModel.java +++ b/vcell-client/src/main/java/cbit/vcell/mapping/gui/ModelProcessSpecsTableModel.java @@ -13,8 +13,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import org.vcell.model.rbm.MolecularTypePattern; import org.vcell.model.rbm.SpeciesPattern; import org.vcell.util.gui.ScrollTable; @@ -27,9 +30,12 @@ import cbit.vcell.mapping.SimulationContext; import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.model.ModelProcess; +import cbit.vcell.model.ProductPattern; +import cbit.vcell.model.ReactantPattern; import cbit.vcell.model.ReactionRule; import cbit.vcell.model.ReactionStep; import cbit.vcell.model.SimpleReaction; +import cbit.vcell.parser.Expression; /** * Insert the type's description here. * Creation date: (2/23/01 10:52:36 PM) @@ -41,8 +47,11 @@ public enum ColumnType { COLUMN_NAME("Name"), COLUMN_DEPICTION("Depiction"), COLUMN_TYPE("Type"), + COLUMN_SUBTYPE("Subtype"), + COLUMN_CONDITION("Condition"), COLUMN_ENABLED("Enabled"), - COLUMN_FAST("Fast"); + COLUMN_FAST("Fast"), + COLUMN_BOND_LENGTH("Bond Length"); public final String label; private ColumnType(String label){ @@ -70,6 +79,9 @@ public String getColumnName(int columnIndex){ private void refreshColumns(){ columns.clear(); columns.addAll(Arrays.asList(ColumnType.values())); // initialize to all columns + columns.remove(ColumnType.COLUMN_BOND_LENGTH); // bond length, subtype and condition are springsalad-only + columns.remove(ColumnType.COLUMN_SUBTYPE); + columns.remove(ColumnType.COLUMN_CONDITION); if(getSimulationContext() == null) { return; } @@ -79,6 +91,12 @@ private void refreshColumns(){ if (getSimulationContext().getApplicationType() == Application.NETWORK_STOCHASTIC){ columns.remove(ColumnType.COLUMN_FAST); } + if(getSimulationContext().getApplicationType() == Application.SPRINGSALAD) { + columns.add(ColumnType.COLUMN_BOND_LENGTH); + columns.add(ColumnType.COLUMN_SUBTYPE); + columns.add(ColumnType.COLUMN_CONDITION); + columns.remove(ColumnType.COLUMN_FAST); + } } @Override public int getColumnCount() { @@ -136,9 +154,18 @@ public Class getColumnClass(int column) { case COLUMN_TYPE:{ return String.class; } + case COLUMN_SUBTYPE:{ + return String.class; + } + case COLUMN_CONDITION:{ + return String.class; + } case COLUMN_ENABLED:{ return Boolean.class; } + case COLUMN_BOND_LENGTH:{ + return Expression.class; + } case COLUMN_FAST:{ return Boolean.class; } @@ -177,9 +204,33 @@ public Object getValueAt(int row, int col) { case COLUMN_TYPE:{ return modelProcessSpec.getModelProcess().getDisplayType(); } + case COLUMN_SUBTYPE:{ + ModelProcess modelProcess = modelProcessSpec.getModelProcess(); + if(getSimulationContext().getApplicationType() == Application.SPRINGSALAD) { + return getSubtype(modelProcessSpec); + } else { + return null; + } + } + case COLUMN_CONDITION:{ + ModelProcess modelProcess = modelProcessSpec.getModelProcess(); + if(getSimulationContext().getApplicationType() == Application.SPRINGSALAD) { + return getTransitionCondition(modelProcessSpec); + } else { + return null; + } + } case COLUMN_ENABLED:{ return new Boolean(!modelProcessSpec.isExcluded()); } + case COLUMN_BOND_LENGTH:{ + ModelProcess modelProcess = modelProcessSpec.getModelProcess(); + if(getSimulationContext().getApplicationType() == Application.SPRINGSALAD) { + return getBondLength(modelProcessSpec); + } else { + return null; + } + } case COLUMN_FAST:{ if (!(modelProcessSpec.getModelProcess() instanceof SimpleReaction)){ return new Boolean(false); @@ -202,11 +253,11 @@ public Object getValueAt(int row, int col) { */ public boolean isCellEditable(int rowIndex, int columnIndex) { ColumnType columnType = columns.get(columnIndex); - switch (columnType){ - case COLUMN_ENABLED:{ + switch (columnType) { + case COLUMN_ENABLED: + case COLUMN_BOND_LENGTH: return true; - } - case COLUMN_FAST:{ + case COLUMN_FAST: { if(getSimulationContext()!=null) { ModelProcessSpec ModelProcessSpec = getValueAt(rowIndex); // @@ -216,7 +267,7 @@ public boolean isCellEditable(int rowIndex, int columnIndex) { } return false; } - default:{ + default: { return false; } } @@ -340,6 +391,17 @@ public void setValueAt(Object aValue, int rowIndex, int columnIndex) { fireTableRowsUpdated(rowIndex,rowIndex); break; } + case COLUMN_BOND_LENGTH: { + if(modelProcessSpec instanceof ReactionSpec) { + ReactionRuleSpec reactionRuleSpec = (ReactionRuleSpec)modelProcessSpec; + if (aValue instanceof String) { + String newExpressionString = (String)aValue; + double result = Double.parseDouble(newExpressionString); + reactionRuleSpec.setFieldBondLength(result); + } + } + break; + } case COLUMN_FAST:{ boolean bFast = ((Boolean)aValue).booleanValue(); if (modelProcessSpec instanceof ReactionSpec){ @@ -359,6 +421,40 @@ public void setValueAt(Object aValue, int rowIndex, int columnIndex) { } } +public static String getSubtype(ModelProcessSpec modelProcessSpec) { + if(modelProcessSpec instanceof ReactionRuleSpec) { + ReactionRuleSpec rrs = (ReactionRuleSpec)modelProcessSpec; + Map analysisResults = new LinkedHashMap<> (); + rrs.analizeReaction(analysisResults); + ReactionRuleSpec.Subtype st = rrs.getSubtype(analysisResults); + return st.columnName; + } else { + return ReactionRuleSpec.Subtype.INCOMPATIBLE.columnName; + } +} +public static String getTransitionCondition(ModelProcessSpec modelProcessSpec) { + if(modelProcessSpec instanceof ReactionRuleSpec) { + ReactionRuleSpec rrs = (ReactionRuleSpec)modelProcessSpec; + Map analysisResults = new LinkedHashMap<> (); + rrs.analizeReaction(analysisResults); + ReactionRuleSpec.TransitionCondition tc = rrs.getTransitionCondition(analysisResults); + if(tc != null) { + return tc.vcellName; + } + } + return null; +} +public double getBondLength(ModelProcessSpec modelProcessSpec) { + if(modelProcessSpec instanceof ReactionRuleSpec) { + ReactionRuleSpec rrs = (ReactionRuleSpec)modelProcessSpec; + double bondLength = rrs.getFieldBondLength(); + return bondLength; + } else { + return -1.0; + } + +} + public Comparator getComparator(int col, boolean ascending) { return new ModelProcessSpecComparator(col, ascending); } diff --git a/vcell-client/src/main/java/cbit/vcell/mapping/gui/MolecularTypeSpecsTableModel.java b/vcell-client/src/main/java/cbit/vcell/mapping/gui/MolecularTypeSpecsTableModel.java new file mode 100644 index 0000000000..17219a48ac --- /dev/null +++ b/vcell-client/src/main/java/cbit/vcell/mapping/gui/MolecularTypeSpecsTableModel.java @@ -0,0 +1,484 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package cbit.vcell.mapping.gui; + +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.swing.DefaultCellEditor; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.SwingConstants; + +import org.vcell.model.rbm.ComponentStatePattern; +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularType; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.util.Displayable; +import org.vcell.util.gui.GuiUtils; +import org.vcell.util.gui.ScrollTable; + +import cbit.gui.ScopedExpression; +import cbit.vcell.client.PopupGenerator; +import cbit.vcell.client.desktop.biomodel.VCellSortTableModel; +import cbit.vcell.geometry.GeometryClass; +import cbit.vcell.mapping.AssignmentRule; +import cbit.vcell.mapping.GeometryContext; +import cbit.vcell.mapping.MolecularInternalLinkSpec; +import cbit.vcell.mapping.RateRule; +import cbit.vcell.mapping.ReactionContext; +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SiteAttributesSpec; +import cbit.vcell.mapping.SpeciesContextSpec; +import cbit.vcell.mapping.StructureMapping; +import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; +import cbit.vcell.mapping.gui.SpeciesContextSpecsTableModel.ColumnType; +import cbit.vcell.mapping.gui.SpeciesContextSpecsTableModel.RulesProvenance; +import cbit.vcell.model.Parameter; +import cbit.vcell.model.SpeciesContext; +import cbit.vcell.model.Structure; +import cbit.vcell.parser.AutoCompleteSymbolFilter; +import cbit.vcell.parser.Expression; +import cbit.vcell.parser.ExpressionBindingException; +import cbit.vcell.parser.ExpressionException; +import cbit.vcell.parser.SymbolTableEntry; +/** + * Insert the type's description here. + * Creation date: (2/23/01 10:52:36 PM) + * @author: + */ +@SuppressWarnings("serial") +public class MolecularTypeSpecsTableModel extends VCellSortTableModel implements java.beans.PropertyChangeListener { + + + private enum ColumnType { + COLUMN_SITE("Site"), + COLUMN_MOLECULE("Molecule"), + COLUMN_STRUCTURE("Location"), + COLUMN_STATE("Initial State"), + COLUMN_RADIUS("Radius"), + COLUMN_DIFFUSION("Diffusion Rate"); + + public final String label; + private ColumnType(String label){ + this.label = label; + } + } + + ArrayList columns = new ArrayList(); + private SimulationContext fieldSimulationContext = null; + private SpeciesContextSpec fieldSpeciesContextSpec = null; + + public MolecularTypeSpecsTableModel(ScrollTable table) { + super(table); + refreshColumns(); + } + + @Override + public Class getColumnClass(int column) { + ColumnType columnType = columns.get(column); + switch (columnType) { + case COLUMN_SITE: + return MolecularComponentPattern.class; + case COLUMN_MOLECULE: + return MolecularType.class; + case COLUMN_STRUCTURE: + return Structure.class; + case COLUMN_STATE: + return ComponentStatePattern.class; + case COLUMN_RADIUS: + case COLUMN_DIFFUSION: + return Expression.class; + default: + return Object.class; + } + } + @Override + public String getColumnName(int columnIndex){ + return columns.get(columnIndex).label; + } + @Override + public int getColumnCount() { + return columns.size(); + } + + @Override + public Object getValueAt(int row, int col) { + try { + if(getSpeciesContextSpec() == null) { + return null; + } + Map siteAttributesMap = getSpeciesContextSpec().getSiteAttributesMap(); + MolecularComponentPattern mcp = getValueAt(row); + SiteAttributesSpec sas = siteAttributesMap.get(mcp); + SpeciesContext sc = fieldSpeciesContextSpec.getSpeciesContext(); + SpeciesPattern sp = sc.getSpeciesPattern(); + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); + ColumnType columnType = columns.get(col); + switch (columnType) { + case COLUMN_SITE: + return mcp.getMolecularComponent().getName(); + case COLUMN_MOLECULE: + return mtp.getMolecularType().getName(); + case COLUMN_STRUCTURE: + if(sas == null) { + return null; + } + return sas.getLocation(); + case COLUMN_STATE: + ComponentStatePattern csp = mcp.getComponentStatePattern(); + if(csp == null) { + return ComponentStatePattern.strNone; + } + if(csp.isAny()) { + return ComponentStatePattern.strAny; + } + String name = csp.getComponentStateDefinition().getName(); + return name; + case COLUMN_RADIUS: + if(sas == null) { + return null; + } + return sas.getRadius(); // nm + case COLUMN_DIFFUSION: + if(sas == null) { + return null; + } + return sas.getDiffusionRate(); // um^2/s + default: + return null; + } + } catch(Exception ex) { + ex.printStackTrace(System.out); + return null; + } + } + + public void setValueAt(Object aValue, int row, int col) { + MolecularComponentPattern mcp = getValueAt(row); + ColumnType columnType = columns.get(col); + switch (columnType) { + case COLUMN_SITE: + case COLUMN_MOLECULE: + case COLUMN_STATE: + return; + case COLUMN_STRUCTURE: + if(aValue instanceof Structure) { + Structure structure = (Structure)aValue; + SiteAttributesSpec sas = getSpeciesContextSpec().getSiteAttributesMap().get(mcp); + if(sas == null) { + sas = new SiteAttributesSpec(fieldSpeciesContextSpec, mcp, structure); + } else { + sas.setLocation(structure); + } + } + return; + case COLUMN_RADIUS: + if (aValue instanceof String) { + String newExpressionString = (String)aValue; + double result = Double.parseDouble(newExpressionString); + SiteAttributesSpec sas = getSpeciesContextSpec().getSiteAttributesMap().get(mcp); + if(sas == null) { + sas = new SiteAttributesSpec(fieldSpeciesContextSpec, mcp, getSpeciesContextSpec().getSpeciesContext().getStructure()); + } + sas.setRadius(result); + } + return; + case COLUMN_DIFFUSION: + if (aValue instanceof String) { + String newExpressionString = (String)aValue; + double result = Double.parseDouble(newExpressionString); + SiteAttributesSpec sas = getSpeciesContextSpec().getSiteAttributesMap().get(mcp); + if(sas == null) { + sas = new SiteAttributesSpec(fieldSpeciesContextSpec, mcp, getSpeciesContextSpec().getSpeciesContext().getStructure()); + } + sas.setDiffusionRate(result); + return; + } + default: + return; + } + } + + @Override + public boolean isCellEditable(int row, int col) { + String siteName = (String)getValueAt(row, 0); + if(SpeciesContextSpec.AnchorSiteString.equals(siteName)) { + // TODO: X, Y, Z, Color must also be non-editable + return false; // row of reserved "Anchor" site is non-editable + } + ColumnType columnType = columns.get(col); + switch (columnType) { + case COLUMN_SITE: + case COLUMN_MOLECULE: + case COLUMN_STATE: + return false; + case COLUMN_STRUCTURE: + case COLUMN_RADIUS: + case COLUMN_DIFFUSION: + return true; + default: + return false; + } + } + + @Override + public Comparator getComparator(final int col, final boolean ascending) { + return new Comparator() { + /** + * Compares its two arguments for order. Returns a negative integer, + * zero, or a positive integer as the first argument is less than, equal + * to, or greater than the second.

+ */ + public int compare(MolecularComponentPattern mcp1, MolecularComponentPattern mcp2){ + + ColumnType columnType = columns.get(col); + switch (columnType) { + case COLUMN_SITE: + case COLUMN_MOLECULE: + case COLUMN_STRUCTURE: + case COLUMN_STATE: + case COLUMN_RADIUS: + case COLUMN_DIFFUSION: + default: + return 1; + } + } + }; + } + + private void refreshColumns() { + columns.clear(); + columns.addAll(Arrays.asList(ColumnType.values())); // initialize to all columns + // TODO: may remove some columns ex: columns.remove(ColumnType.COLUMN_STRUCTURE) + } + + public void setSimulationContext(SimulationContext simulationContext) { + SimulationContext oldValue = fieldSimulationContext; + int oldColumnCount = getColumnCount(); + if (oldValue != null) { + oldValue.removePropertyChangeListener(this); + oldValue.getGeometryContext().removePropertyChangeListener(this); + updateListenersReactionContext(oldValue.getReactionContext(),true); + } + fieldSimulationContext = simulationContext; + refreshColumns(); + int newColumnCount = getColumnCount(); + if (oldColumnCount != newColumnCount) { + fireTableStructureChanged(); + } + if (simulationContext != null) { + simulationContext.addPropertyChangeListener(this); + simulationContext.getGeometryContext().addPropertyChangeListener(this); + updateListenersReactionContext(simulationContext.getReactionContext(),false); + +// autoCompleteSymbolFilter = simulationContext.getAutoCompleteSymbolFilter(); + refreshData(); + } + } + private SimulationContext getSimulationContext() { + return fieldSimulationContext; + } + + public void setSpeciesContextSpec(SpeciesContextSpec speciesContextSpec) { + SpeciesContextSpec oldValue = fieldSpeciesContextSpec; + int oldColumnCount = getColumnCount(); + if (oldValue != null) { + oldValue.removePropertyChangeListener(this); + } + fieldSpeciesContextSpec = speciesContextSpec; +// initializeForSpringSaLaD(); + refreshColumns(); + int newColumnCount = getColumnCount(); + if (oldColumnCount != newColumnCount) { + fireTableStructureChanged(); + } + if (speciesContextSpec != null) { + speciesContextSpec.addPropertyChangeListener(this); + refreshData(); + } + } + private SpeciesContextSpec getSpeciesContextSpec() { + return fieldSpeciesContextSpec; + } + + + private void updateListenersReactionContext(ReactionContext reactionContext,boolean bRemove) { + + if(bRemove){ + reactionContext.removePropertyChangeListener(this); + SpeciesContextSpec oldSpecs[] = reactionContext.getSpeciesContextSpecs(); + for (int i=0;i internalLinkSet = getSpeciesContextSpec().getInternalLinkSet(); +// Map siteAttributesMap = getSpeciesContextSpec().getSiteAttributesMap(); +// MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); +// MolecularType mt = mtp.getMolecularType(); +// List componentList = mt.getComponentList(); +// for(MolecularComponent mc : componentList) { +// MolecularComponentPattern mcp = mtp.getMolecularComponentPattern(mc); +// SiteAttributesSpec sas = siteAttributesMap.get(mcp); +// if(sas == null || sas.getMolecularComponentPattern() == null) { +// sas = new SiteAttributesSpec(fieldSpeciesContextSpec, mcp, getSpeciesContextSpec().getSpeciesContext().getStructure()); +// siteAttributesMap.put(mcp, sas); +// } +// } +// if(internalLinkSet.isEmpty()) { +// for(int i=0; i< componentList.size()-1; i++) { +// MolecularComponent mcOne = componentList.get(i); +// MolecularComponent mcTwo = componentList.get(i+1); +// MolecularComponentPattern mcpOne = mtp.getMolecularComponentPattern(mcOne); +// MolecularComponentPattern mcpTwo = mtp.getMolecularComponentPattern(mcTwo); +// MolecularInternalLinkSpec link = new MolecularInternalLinkSpec(fieldSpeciesContextSpec, mcpOne, mcpTwo); +// // TODO: set x,y,z instead, link will be computed +//// link.setLinkLength(2.0); +// internalLinkSet.add(link); +// } +// } +// } +// } + + private void refreshData() { + List molecularComponentPatternList = computeData(); + setData(molecularComponentPatternList); + GuiUtils.flexResizeTableColumns(ownerTable); + + updateLocationComboBox(); + } + + private void updateLocationComboBox() { + if(fieldSimulationContext == null) { + return; + } + DefaultComboBoxModel aModel = new DefaultComboBoxModel<>(); + Structure[] structures = fieldSimulationContext.getGeometryContext().getModel().getStructures(); + for(Structure structure : structures) { + aModel.addElement(structure); + } + JComboBox locationComboBoxCellEditor = new JComboBox<>(); + locationComboBoxCellEditor.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + setHorizontalTextPosition(SwingConstants.LEFT); + if (value instanceof Structure) { + Structure structure = (Structure)value; + setText(structure.getName()); + + } + return this; + } + }); + locationComboBoxCellEditor.setModel(aModel); + ownerTable.getColumnModel().getColumn(ColumnType.COLUMN_STRUCTURE.ordinal()).setCellEditor(new DefaultCellEditor(locationComboBoxCellEditor)); + } + + protected List computeData() { + ArrayList allParameterList = new ArrayList(); + if(fieldSpeciesContextSpec != null && fieldSpeciesContextSpec.getSpeciesContext() != null) { + SpeciesPattern sp = fieldSpeciesContextSpec.getSpeciesContext().getSpeciesPattern(); + if(sp == null) { + return null; + } + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); + MolecularType mt = mtp.getMolecularType(); + List componentList = mt.getComponentList(); + for(MolecularComponent mc : componentList) { + MolecularComponentPattern mcp = mtp.getMolecularComponentPattern(mc); + allParameterList.add(mcp); + } + } else { + return null; + } + return allParameterList; +// boolean bSearchInactive = searchText == null || searchText.length() == 0; +// if(bSearchInactive){ +// return allParameterList; +// } +// String lowerCaseSearchText = bSearchInactive ? null : searchText.toLowerCase(); +// ArrayList parameterList = new ArrayList(); +// for (SpeciesContextSpec parameter : allParameterList) { +// if (bSearchInactive +// || parameter.getSpeciesContext().getName().toLowerCase().contains(lowerCaseSearchText) +// /*|| parameter.getSpeciesContext().getStructure().getName().toLowerCase().contains(lowerCaseSearchText)*/) { +// parameterList.add(parameter); +// } +// } +// return parameterList; + } + + + + + + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getSource() instanceof ReactionContext && evt.getPropertyName().equals("speciesContextSpecs")) { + refreshData(); + } + if (evt.getSource() instanceof SpeciesContext && evt.getPropertyName().equals("name")) { + fireTableRowsUpdated(0,getRowCount()-1); + } + + if (evt.getSource() instanceof SpeciesContextSpec) { + fireTableRowsUpdated(0,getRowCount()-1); + } + if (evt.getSource() instanceof SpeciesContextSpec.SpeciesContextSpecParameter) { + fireTableRowsUpdated(0,getRowCount()-1); + } + if (evt.getSource() instanceof GeometryContext) { + refreshColumns(); + fireTableStructureChanged(); + } + + } + + +} diff --git a/vcell-client/src/main/java/cbit/vcell/mapping/gui/SpeciesContextSpecsTableModel.java b/vcell-client/src/main/java/cbit/vcell/mapping/gui/SpeciesContextSpecsTableModel.java index 8f3ce86fd8..99c61cfe69 100644 --- a/vcell-client/src/main/java/cbit/vcell/mapping/gui/SpeciesContextSpecsTableModel.java +++ b/vcell-client/src/main/java/cbit/vcell/mapping/gui/SpeciesContextSpecsTableModel.java @@ -82,6 +82,8 @@ private ColumnType(String label){ private SimulationContext fieldSimulationContext = null; private AutoCompleteSymbolFilter autoCompleteSymbolFilter = null; + + private boolean bEditable = true; // this.isCellEditable() decides /** * ReactionSpecsTableModel constructor comment. @@ -175,6 +177,12 @@ private void refreshColumns(){ columns.remove(ColumnType.COLUMN_CLAMPED); columns.remove(ColumnType.COLUMN_FORCECONTINUOUS); } + if (getSimulationContext() == null || getSimulationContext().getApplicationType().equals(SimulationContext.Application.SPRINGSALAD)) { + columns.remove(ColumnType.COLUMN_DIFFUSION); + columns.remove(ColumnType.COLUMN_FORCECONTINUOUS); + columns.remove(ColumnType.COLUMN_WELLMIXED); + columns.remove(ColumnType.COLUMN_RULES); + } } /** * getColumnCount method comment. @@ -276,6 +284,9 @@ public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } case COLUMN_CLAMPED:{ + if(!bEditable) { + return false; // the table wants this column un-editable + } return true; } case COLUMN_RULES:{ @@ -293,6 +304,9 @@ public boolean isCellEditable(int rowIndex, int columnIndex) { if(/*rr != null || */ar != null) { return false; } + if(!bEditable) { + return false; + } return true; } case COLUMN_DIFFUSION: { @@ -688,6 +702,13 @@ public int compare(SpeciesContextSpec speciesContextSpec1, SpeciesContextSpec sp }; } +public void setEditable(boolean bEditable) { + this.bEditable = bEditable; +} +public boolean isEfitable() { + return bEditable; +} + public static class TableUtil { // detects whether expressions within this column contain numbers, alphanumeric expressions or a mix // and sorts accordingly (numbers first (sorted numerically), alphanumeric expr next (sorted alphabetically w. ignore case)) diff --git a/vcell-client/src/main/java/cbit/vcell/math/gui/MathDescEditor.java b/vcell-client/src/main/java/cbit/vcell/math/gui/MathDescEditor.java index b64f08a662..aa0f9fecb0 100644 --- a/vcell-client/src/main/java/cbit/vcell/math/gui/MathDescEditor.java +++ b/vcell-client/src/main/java/cbit/vcell/math/gui/MathDescEditor.java @@ -532,6 +532,16 @@ private static Set getAutoCompletionWords() { autoCompletionWords.add(VCML.ParticleDriftX); autoCompletionWords.add(VCML.ParticleDriftY); autoCompletionWords.add(VCML.ParticleDriftZ); + autoCompletionWords.add(VCML.Subtype); + autoCompletionWords.add(VCML.TransitionCondition); + autoCompletionWords.add(VCML.BondLength); + autoCompletionWords.add(VCML.Links); + autoCompletionWords.add(VCML.LinkSeparator); + autoCompletionWords.add(VCML.ParticleComponentRadius); + autoCompletionWords.add(VCML.ParticleComponentDiffusionRate); + autoCompletionWords.add(VCML.ParticleComponentLocation); + autoCompletionWords.add(VCML.ParticleComponentCoordinate); + autoCompletionWords.add(VCML.ParticleComponentColor); autoCompletionWords.add(VCML.PostProcessingBlock); autoCompletionWords.add(VCML.ExplicitDataGenerator); @@ -832,6 +842,15 @@ public static Set getkeywords() { keywords.add(VCML.ParticleDriftX); keywords.add(VCML.ParticleDriftY); keywords.add(VCML.ParticleDriftZ); + keywords.add(VCML.Subtype); + keywords.add(VCML.TransitionCondition); + keywords.add(VCML.BondLength); + keywords.add(VCML.Links); + keywords.add(VCML.ParticleComponentRadius); + keywords.add(VCML.ParticleComponentDiffusionRate); + keywords.add(VCML.ParticleComponentLocation); + keywords.add(VCML.ParticleComponentCoordinate); + keywords.add(VCML.ParticleComponentColor); keywords.add(VCML.PostProcessingBlock); keywords.add(VCML.ExplicitDataGenerator); diff --git a/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/LangevinOptionsPanel.java b/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/LangevinOptionsPanel.java new file mode 100644 index 0000000000..2aaeb7d2f0 --- /dev/null +++ b/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/LangevinOptionsPanel.java @@ -0,0 +1,397 @@ +package cbit.vcell.solver.ode.gui; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.vcell.util.gui.CollapsiblePanel; +import org.vcell.util.gui.DialogUtils; + +import cbit.vcell.client.PopupGenerator; +import cbit.vcell.solver.LangevinSimulationOptions; +import cbit.vcell.solver.OutputTimeSpec; +import cbit.vcell.solver.SolverTaskDescription; + +@SuppressWarnings("serial") +public class LangevinOptionsPanel extends CollapsiblePanel { + + private SolverTaskDescription solverTaskDescription = null; + + private javax.swing.JRadioButton trajectoryRadioButton = null; + private javax.swing.JRadioButton multiRunRadioButton = null; + private javax.swing.ButtonGroup buttonGroupTrials = null; + + private JLabel numOfTrialsLabel = null; + private JTextField ivjJTextFieldNumOfTrials = null; + + private JTextField intervalImageTextField = null; + private JTextField intervalSpringTextField = null; + + private IvjEventHandler ivjEventHandler = new IvjEventHandler(); + + + private class IvjEventHandler implements java.awt.event.ActionListener, java.awt.event.FocusListener, java.beans.PropertyChangeListener { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + if (evt.getSource() == LangevinOptionsPanel.this && (evt.getPropertyName().equals("solverTaskDescription"))) { + refresh(); + } + if (evt.getSource() == solverTaskDescription && (evt.getPropertyName().equals(SolverTaskDescription.PROPERTY_SOLVER_DESCRIPTION))) { + refresh(); + } + if (evt.getSource() == solverTaskDescription && evt.getPropertyName().equals(SolverTaskDescription.PROPERTY_LANGEVIN_SIMULATION_OPTIONS)) { + refresh(); + } + } + + public void actionPerformed(java.awt.event.ActionEvent e) { +// if (e.getSource() == getCustomizedSeedRadioButton() || e.getSource() == getRandomSeedRadioButton()) { +// setNewOptions(); +// } + if (e.getSource() == getTrajectoryButton()) { + getJTextFieldNumOfTrials().setEnabled(false); + setNewOptions(); + } else if (e.getSource() == getMultiRunButton()) { + getJTextFieldNumOfTrials().setEnabled(true); + setNewOptions(); + } + } + + public void focusGained(java.awt.event.FocusEvent e) { + } + + public void focusLost(java.awt.event.FocusEvent e) { + if (e.isTemporary()) { + return; + } + if (e.getSource() == getJTextFieldNumOfTrials()) { + setNewOptions(); + } + } + } + + public LangevinOptionsPanel() { + super("Langevin Options Panel"); + addPropertyChangeListener(ivjEventHandler); + initialize(); + } + + private void initialize() { + try { + + getContentPanel().setLayout(new GridBagLayout()); + + JPanel trialPanel = new JPanel(new GridBagLayout()); // left panel + trialPanel.setLayout(new GridBagLayout()); + JPanel centerPanel = new JPanel(new GridBagLayout()); + centerPanel.setLayout(new GridBagLayout()); + JPanel rightPanel = new JPanel(new GridBagLayout()); // fake panel, for looks + rightPanel.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; +// gbc.weightx = 1.0; +// gbc.weighty = 1.0; + gbc.insets = new Insets(1,1,1,1); + getContentPanel().add(trialPanel, gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; +// gbc.weightx = 1.0; +// gbc.weighty = 1.0; + gbc.insets = new Insets(1,1,1,1); + getContentPanel().add(centerPanel, gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.insets = new Insets(1,1,1,1); + getContentPanel().add(rightPanel, gbc); + + // ----- trialPanel -------------------------------------------------------------- + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.gridwidth = 4; + gbc.insets = new Insets(2,1,0,1); + trialPanel.add(getTrajectoryButton(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,1,1,1); + trialPanel.add(getMultiRunButton(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,22,1,6); + trialPanel.add(getNumOfTrialsLabel(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,5,3,1); + trialPanel.add(getJTextFieldNumOfTrials(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.EAST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1.0; + trialPanel.add(new JLabel(""), gbc); + + // ----- centerPanel ----------------------------------------------------- + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,26,1,5); + centerPanel.add(new JLabel("Spring Interval"), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,5,3,1); + centerPanel.add(getJTextFieldIntervalSpring(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0,6,1,22); + centerPanel.add(new JLabel("s"), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,26,1,5); + centerPanel.add(new JLabel("Image Interval"), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(0,5,3,1); + centerPanel.add(getJTextFieldIntervalImage(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0,6,1,22); + centerPanel.add(new JLabel("s"), gbc); + + // ----- rightPanel ---------------------------------------------------- + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.BOTH; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.insets = new Insets(0,22,1,6); + rightPanel.add(new JLabel(""), gbc); // fake, just for looks + + // ---------------------------------------------------------------------- + getButtonGroupTrials().add(getTrajectoryButton()); + getButtonGroupTrials().add(getMultiRunButton()); + getButtonGroupTrials().setSelected(getTrajectoryButton().getModel(), true); + + getJTextFieldNumOfTrials().setEnabled(false); + + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + + private javax.swing.ButtonGroup getButtonGroupTrials() { + if (buttonGroupTrials == null) { + try { + buttonGroupTrials = new javax.swing.ButtonGroup(); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return buttonGroupTrials; + } + private javax.swing.JRadioButton getTrajectoryButton() { + if (trajectoryRadioButton == null) { + try { + trajectoryRadioButton = new javax.swing.JRadioButton(); + trajectoryRadioButton.setName("Trajectory"); + trajectoryRadioButton.setText("Single Trajectory"); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return trajectoryRadioButton; + } + private javax.swing.JRadioButton getMultiRunButton() { + if (multiRunRadioButton == null) { + try { + multiRunRadioButton = new javax.swing.JRadioButton(); + multiRunRadioButton.setName("MultiRun"); + multiRunRadioButton.setText("Multiple Runs"); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return multiRunRadioButton; + } + private javax.swing.JTextField getJTextFieldNumOfTrials() { + if (ivjJTextFieldNumOfTrials == null) { + try { + ivjJTextFieldNumOfTrials = new javax.swing.JTextField(); + ivjJTextFieldNumOfTrials.setName("JTextFieldNumOfTrials"); + ivjJTextFieldNumOfTrials.setColumns(9); + ivjJTextFieldNumOfTrials.setText("100"); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return ivjJTextFieldNumOfTrials; + } + private javax.swing.JLabel getNumOfTrialsLabel() { + if (numOfTrialsLabel == null) { + try { + numOfTrialsLabel = new javax.swing.JLabel(); + numOfTrialsLabel.setName("NumOfTrials"); + numOfTrialsLabel.setText("Num. Of Trials"); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return numOfTrialsLabel; + } + private Component getJTextFieldIntervalImage() { + if (intervalImageTextField == null) { + try { + intervalImageTextField = new javax.swing.JTextField(); + intervalImageTextField.setName("IntervalImageTextField"); + intervalImageTextField.setColumns(9); + intervalImageTextField.setText("1.00E-4"); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return intervalImageTextField; + } + + private Component getJTextFieldIntervalSpring() { + if (intervalSpringTextField == null) { + try { + intervalSpringTextField = new javax.swing.JTextField(); + intervalSpringTextField.setName("IntervalSpringTextField"); + intervalSpringTextField.setColumns(9); + intervalSpringTextField.setText("1.00E-9"); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return intervalSpringTextField; + } + + + private void handleException(java.lang.Throwable exception) { + /* Uncomment the following lines to print uncaught exceptions to stdout */ + System.out.println("--------- UNCAUGHT EXCEPTION ---------"); + exception.printStackTrace(System.out); + } + + public final void setSolverTaskDescription(SolverTaskDescription newValue) { + SolverTaskDescription oldValue = solverTaskDescription; + /* Stop listening for events from the current object */ + if (oldValue != null) { + oldValue.removePropertyChangeListener(ivjEventHandler); + } + solverTaskDescription = newValue; + + /* Listen for events from the new object */ + if (newValue != null) { + newValue.addPropertyChangeListener(ivjEventHandler); + } + solverTaskDescription = newValue; + firePropertyChange("solverTaskDescription", oldValue, newValue); + + initConnections(); + } + private void initConnections() { + getTrajectoryButton().addActionListener(ivjEventHandler); + getMultiRunButton().addActionListener(ivjEventHandler); + getJTextFieldNumOfTrials().addFocusListener(ivjEventHandler); + + + } + + private void refresh() { + if (solverTaskDescription == null) { + return; + } + if(!solverTaskDescription.getSolverDescription().isLangevinSolver()) { + setVisible(false); + return; + } + setVisible(true); + + LangevinSimulationOptions lso = solverTaskDescription.getLangevinSimulationOptions(); + + // TODO: temporarily disable the button + // UNDO THIS WHEN DEVELOPMENT IS COMPLETE + if(solverTaskDescription.getSolverDescription().isNFSimSolver()) { + getMultiRunButton().setEnabled(false); + return; + } + + + } + + private void setNewOptions() { + if(!isVisible()) { + return; + } + try { + LangevinSimulationOptions sso = solverTaskDescription.getLangevinSimulationOptions(); + int numTrials = 1; + + if(getMultiRunButton().isSelected()) { + numTrials = Integer.parseInt(getJTextFieldNumOfTrials().getText()); + } + sso.setNumOfTrials(numTrials); + + // make a copy + LangevinSimulationOptions lso = new LangevinSimulationOptions(sso); + solverTaskDescription.setLangevinSimulationOptions(lso); + + } catch(Exception e) { + PopupGenerator.showErrorDialog(this, e.getMessage(), e); + } + } + +} diff --git a/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/OutputOptionsPanel.java b/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/OutputOptionsPanel.java index a631c91b6c..f4f69a1aef 100644 --- a/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/OutputOptionsPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/OutputOptionsPanel.java @@ -89,6 +89,9 @@ public void propertyChange(java.beans.PropertyChangeEvent evt) { if (evt.getSource() == solverTaskDescription && evt.getPropertyName().equals(SolverTaskDescription.PROPERTY_NFSIM_SIMULATION_OPTIONS)) { refresh(); } + if (evt.getSource() == solverTaskDescription && evt.getPropertyName().equals(SolverTaskDescription.PROPERTY_LANGEVIN_SIMULATION_OPTIONS)) { + refresh(); + } } public void actionPerformed(java.awt.event.ActionEvent e) { @@ -640,7 +643,8 @@ private void refresh() { BeanUtils.enableComponents(getUniformOutputPanel(), false); if (solverTaskDescription.getSolverDescription().equals(SolverDescription.Smoldyn) || - solverTaskDescription.getSolverDescription().equals(SolverDescription.NFSim) ) { + solverTaskDescription.getSolverDescription().equals(SolverDescription.NFSim) || + solverTaskDescription.getSolverDescription().equals(SolverDescription.Langevin) ) { getDefaultOutputPanel().setVisible(false); getDefaultOutputRadioButton().setVisible(false); } else if (solverTaskDescription.getSolverDescription().isChomboSolver()) { diff --git a/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/SolverTaskDescriptionAdvancedPanel.java b/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/SolverTaskDescriptionAdvancedPanel.java index c29dd613aa..c0901fc7c5 100644 --- a/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/SolverTaskDescriptionAdvancedPanel.java +++ b/vcell-client/src/main/java/cbit/vcell/solver/ode/gui/SolverTaskDescriptionAdvancedPanel.java @@ -72,6 +72,7 @@ public class SolverTaskDescriptionAdvancedPanel extends javax.swing.JPanel { private OutputOptionsPanel ivjOutputOptionsPanel = null; private StochSimOptionsPanel stochSimOptionsPanel = null; private NFSimGeneralOptionsPanel nFSimGeneralOptionsPanel = null; + private LangevinOptionsPanel langevinOptionsPanel = null; private SmoldynSimulationOptionsPanel smoldynSimulationOptionsPanel = null; private NFSimSimulationOptionsPanel nfsimSimulationOptionsPanel = null; private SundialsPdeSolverOptionsPanel sundialsPdeSolverOptionsPanel = null; @@ -459,6 +460,17 @@ private NFSimSimulationOptionsPanel getNFSimSimulationOptionsPanel() { return nfsimSimulationOptionsPanel; } +private LangevinOptionsPanel getLangevinOptionsPanel() { + if (langevinOptionsPanel == null) { + try { + langevinOptionsPanel = new LangevinOptionsPanel(); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return langevinOptionsPanel; +} + private SmoldynSimulationOptionsPanel getSmoldynSimulationOptionsPanel() { if (smoldynSimulationOptionsPanel == null) { try { @@ -874,6 +886,15 @@ private void initialize() { gbc.insets = new java.awt.Insets(1, 4, 2, 4); add(getNFSimGeneralOptionsPanel(), gbc); + gridy ++; + gbc = new java.awt.GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = gridy; + gbc.fill = java.awt.GridBagConstraints.BOTH; + gbc.weightx = 1.0; + gbc.insets = new java.awt.Insets(1, 4, 2, 4); + add(getLangevinOptionsPanel(), gbc); + gridy ++; java.awt.GridBagConstraints constraintsJPanel1 = new java.awt.GridBagConstraints(); constraintsJPanel1.gridx = 0; @@ -1027,6 +1048,7 @@ private void setTornOffSolverTaskDescription(SolverTaskDescription newValue) { getStochSimOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription); getNFSimGeneralOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription); getNFSimSimulationOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription); + getLangevinOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription); getSmoldynSimulationOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription); getOutputOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription, unitInfo); getSundialsPdeSolverOptionsPanel().setSolverTaskDescription(ivjTornOffSolverTaskDescription); diff --git a/vcell-client/src/main/java/org/vcell/model/springsalad/gui/AddLinkPanel.java b/vcell-client/src/main/java/org/vcell/model/springsalad/gui/AddLinkPanel.java new file mode 100644 index 0000000000..ffce0fe304 --- /dev/null +++ b/vcell-client/src/main/java/org/vcell/model/springsalad/gui/AddLinkPanel.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package org.vcell.model.springsalad.gui; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; + +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.NetworkConstraints; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.util.gui.DialogUtils; +import org.vcell.util.gui.EditorScrollTable; + +import cbit.vcell.client.ChildWindowManager.ChildWindow; +import cbit.vcell.client.desktop.biomodel.DocumentEditorSubPanel; +import cbit.vcell.mapping.MolecularInternalLinkSpec; +import cbit.vcell.mapping.SpeciesContextSpec; +import cbit.vcell.mapping.gui.NetworkConstraintsTableModel; +import cbit.vcell.mapping.gui.StoichiometryTableModel; + +@SuppressWarnings("serial") +public class AddLinkPanel extends DocumentEditorSubPanel { + + enum ActionButtons { + Apply, + Cancel + } + ActionButtons buttonPushed = ActionButtons.Cancel; + + private EventHandler eventHandler = new EventHandler(); + + private JButton applyButton; + private JButton cancelButton; + + private final MolecularStructuresPanel owner; + private ChildWindow parentChildWindow; + + private JList firstSiteList = null; + private DefaultListModel firstSiteListModel = new DefaultListModel<>(); + private ListCellRenderer firstSiteCellRenderer = new DefaultListCellRenderer(){ + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof MolecularComponentPattern && component instanceof JLabel) { + MolecularComponentPattern mcp = (MolecularComponentPattern)value; + ((JLabel)component).setText(mcp.getMolecularComponent().getName()); + } + return component; + } + }; + private JList secondSiteList = null; + private DefaultListModel secondSiteListModel = new DefaultListModel<>(); + private ListCellRenderer secondSiteCellRenderer = new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof MolecularComponentPattern && component instanceof JLabel){ + MolecularComponentPattern mcp = (MolecularComponentPattern)value; + ((JLabel)component).setText(mcp.getMolecularComponent().getName()); + } + return component; + } + }; + + + private class EventHandler implements FocusListener, ActionListener, ListSelectionListener { + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == null) { + ; + } + } + @Override + public void focusGained(FocusEvent e) { + } + @Override + public void focusLost(FocusEvent e) { + if (e.getSource() == null) { + ; + } + } + @Override + public void valueChanged(ListSelectionEvent e) { + if(e.getSource() == firstSiteList || e.getSource() == secondSiteList) { + MolecularComponentPattern firstMcp = firstSiteList.getSelectedValue(); + MolecularComponentPattern secondMcp = secondSiteList.getSelectedValue(); + if(firstMcp != null && secondMcp != null) { + getApplyButton().setEnabled(true); + } else { + getApplyButton().setEnabled(false); + } + } + } + } + +public AddLinkPanel(MolecularStructuresPanel owner) { + super(); + this.owner = owner; + initialize(); +} + + +private void handleException(java.lang.Throwable exception) { + System.out.println("--------- UNCAUGHT EXCEPTION ---------"); + exception.printStackTrace(System.out); +} + +private void initialize() { + try { + SpeciesContextSpec scs = owner.getSpeciesContextSpec(); + SpeciesPattern sp = scs.getSpeciesContext().getSpeciesPattern(); + if(sp == null) { + DialogUtils.showErrorDialog(parentChildWindow.getParent(), "SpeciesPattern is null"); + return; + } + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); + if(mtp == null) { + DialogUtils.showErrorDialog(parentChildWindow.getParent(), "No molecule defined"); + return; + } + if(mtp.getComponentPatternList().size() < 2) { + DialogUtils.showErrorDialog(parentChildWindow.getParent(), "At least 2 Sites are needed"); + return; + } + + firstSiteList = new JList(firstSiteListModel); + firstSiteList.setCellRenderer(firstSiteCellRenderer); + secondSiteList = new JList(secondSiteListModel); + secondSiteList.setCellRenderer(secondSiteCellRenderer); + firstSiteList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + secondSiteList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + for(MolecularComponentPattern mcp : mtp.getComponentPatternList()) { + firstSiteListModel.addElement(mcp); + secondSiteListModel.addElement(mcp); + } + + + JPanel p = new JPanel(); + p.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(6, 8, 1, 1); // top, left, bottom, right + p.add(new JLabel("First Site"), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(6, 0, 0, 10); + p.add(new JLabel("Second Site"), gbc); + + JScrollPane scrollPane1 = new JScrollPane(firstSiteList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(5, 2, 2, 3); + p.add(scrollPane1, gbc); + + JScrollPane scrollPane2 = new JScrollPane(secondSiteList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(5, 2, 2, 3); + p.add(scrollPane2, gbc); + + // -------------------------------------------------------------------- + setName("AddLinkPanel"); + setLayout(new GridBagLayout()); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.gridwidth = 3; + gbc.anchor = GridBagConstraints.NORTHWEST; + add(p, gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(6, 2, 8, 2); + add(getApplyButton(), gbc); + + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(6, 2, 8, 10); + add(getCancelButton(), gbc); + + // ------------------------------------------------ + + firstSiteList.addListSelectionListener(eventHandler); + secondSiteList.addListSelectionListener(eventHandler); + + getApplyButton().setEnabled(false); + + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } +} + + +public ActionButtons getButtonPushed() { + return buttonPushed; +} +private JButton getApplyButton() { + if (applyButton == null) { + applyButton = new javax.swing.JButton("Apply"); + applyButton.setName("ApplyButton"); + applyButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + buttonPushed = ActionButtons.Apply; + parentChildWindow.close(); + } + }); + } + return applyButton; +} +private JButton getCancelButton() { + if (cancelButton == null) { + cancelButton = new javax.swing.JButton("Cancel"); + cancelButton.setName("CancelButton"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + buttonPushed = ActionButtons.Cancel; + parentChildWindow.close(); + } + }); + } + return cancelButton; +} + +public JList getFirstSiteList() { + return firstSiteList; +} +public JList getSecondSiteList() { + return secondSiteList; +} + +@Override +protected void onSelectedObjectsChange(Object[] selectedObjects) { + +} + +public void setChildWindow(ChildWindow childWindow) { + this.parentChildWindow = childWindow; +} + +} diff --git a/vcell-client/src/main/java/org/vcell/model/springsalad/gui/MolecularStructuresPanel.java b/vcell-client/src/main/java/org/vcell/model/springsalad/gui/MolecularStructuresPanel.java index 9fdb40a11b..a914a01368 100644 --- a/vcell-client/src/main/java/org/vcell/model/springsalad/gui/MolecularStructuresPanel.java +++ b/vcell-client/src/main/java/org/vcell/model/springsalad/gui/MolecularStructuresPanel.java @@ -1,8 +1,9 @@ package org.vcell.model.springsalad.gui; +import java.awt.Color; import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -13,24 +14,64 @@ import java.beans.PropertyChangeListener; import javax.swing.BorderFactory; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.DefaultListSelectionModel; +import javax.swing.Icon; import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; import javax.swing.JTextField; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; import javax.swing.border.Border; import javax.swing.border.EtchedBorder; import javax.swing.border.TitledBorder; - +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableModel; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.util.gui.DefaultScrollTableCellRenderer; +import org.vcell.util.gui.EditorScrollTable; +import org.vcell.util.gui.VCellIcons; +import org.vcell.util.gui.ScrollTable.ScrollTableBooleanCellRenderer; +import org.vcell.util.gui.sorttable.SortTableModel; + +import cbit.gui.ScopedExpression; +import cbit.vcell.client.ChildWindowManager; +import cbit.vcell.client.ChildWindowManager.ChildWindow; import cbit.vcell.client.desktop.biomodel.ApplicationSpecificationsPanel; import cbit.vcell.client.desktop.biomodel.DocumentEditorSubPanel; import cbit.vcell.client.desktop.biomodel.IssueManager; -import cbit.vcell.client.desktop.biomodel.SelectionManager; +import cbit.vcell.client.desktop.biomodel.VCellSortTableModel; import cbit.vcell.client.desktop.biomodel.SelectionManager.ActiveViewID; +import cbit.vcell.graph.SmallShapeManager; +import cbit.vcell.graph.SpeciesPatternSmallShape; +import cbit.vcell.mapping.AssignmentRule; +import cbit.vcell.mapping.MolecularInternalLinkSpec; +import cbit.vcell.mapping.RateRule; import cbit.vcell.mapping.SimulationContext; -import cbit.vcell.mapping.TaskCallbackMessage; -import cbit.vcell.mapping.TaskCallbackMessage.TaskCallbackStatus; +import cbit.vcell.mapping.SiteAttributesSpec; +import cbit.vcell.mapping.SpeciesContextSpec; +import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; +import cbit.vcell.mapping.gui.MolecularTypeSpecsTableModel; +import cbit.vcell.mapping.gui.SpeciesContextSpecsTableModel; +import cbit.vcell.mapping.gui.StructureMappingTableRenderer.TextIcon; import cbit.vcell.model.Model; +import cbit.vcell.model.Species; +import cbit.vcell.model.SpeciesContext; +import cbit.vcell.model.Structure; +import cbit.vcell.model.Model.RbmModelContainer; +import cbit.vcell.units.VCUnitDefinition; // we should use WindowBuilder Plugin (add it to Eclipse IDE) to speed up panel design // can choose absolute layout and place everything exactly as we see fit @@ -39,20 +80,120 @@ public class MolecularStructuresPanel extends DocumentEditorSubPanel implements private EventHandler eventHandler = new EventHandler(); private SimulationContext fieldSimulationContext; - private IssueManager fieldIssueManager; - private SelectionManager fieldSelectionManager; + private SpeciesContextSpec fieldSpeciesContextSpec; + private MolecularComponentPattern fieldMolecularComponentPattern; - private class EventHandler implements FocusListener, ActionListener, PropertyChangeListener { + private EditorScrollTable speciesContextSpecsTable = null; + private SpeciesContextSpecsTableModel speciesContextSpecsTableModel = null; + private SmallShapeManager shapeManager = new SmallShapeManager(false, false, false, false); - public void actionPerformed(ActionEvent e) { + private EditorScrollTable molecularTypeSpecsTable = null; + private MolecularTypeSpecsTableModel molecularTypeSpecsTableModel = null; + + private JComboBox siteColorComboBox = null; + private JTextField siteXField = null; + private JTextField siteYField = null; + private JTextField siteZField = null; + + private JTextField linkLengthField = null; + private JButton addLinkButton = null; + + // + // TODO: make it possible to use right click menu to delete links + // + private JList siteLinksList = null; + private DefaultListModel siteLinksListModel = new DefaultListModel<>(); + private ListCellRenderer siteLinksCellRenderer = new DefaultListCellRenderer(){ + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof MolecularInternalLinkSpec && component instanceof JLabel){ + MolecularInternalLinkSpec mils = (MolecularInternalLinkSpec)value; + MolecularComponentPattern firstMcp = mils.getMolecularComponentPatternOne(); + MolecularComponentPattern secondtMcp = mils.getMolecularComponentPatternTwo(); + ((JLabel)component).setText(firstMcp.getMolecularComponent().getName() + " :: " + secondtMcp.getMolecularComponent().getName()); + } + return component; + } + }; + + // TODO: this is for popup menus in the table (instantiated in getMolecularTypeSpecsTable() - uncomment there too) +// private class InternalScrollTableActionManager extends DefaultScrollTableActionManager { +// InternalScrollTableActionManager(JTable table) { +// super(table); +// ApplicationSpecificationsPanel asp; +// } +// @Override +// protected void constructPopupMenu() { +// if (popupMenu == null) { +// super.constructPopupMenu(); +// int pos = 0; +// DocumentEditorSubPanel.addFieldDataMenuItem(getOwnerTable(), popupMenu, pos++); +// popupMenu.insert(new JSeparator(), pos++); +// } +// Object obj = VCellTransferable.getFromClipboard(VCellTransferable.OBJECT_FLAVOR); +// boolean bPastable = obj instanceof VCellTransferable.ResolvedValuesSelection; +// boolean bSomethingSelected = getSpeciesContextSpecsTable().getSelectedRows() != null && getSpeciesContextSpecsTable().getSelectedRows().length > 0; +// } +// } + + private class EventHandler implements FocusListener, ActionListener, PropertyChangeListener, ListSelectionListener { + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + if (source == siteXField || source == siteYField || source == siteZField) { + changePosition(); + } else if(source == linkLengthField) { + changeLinkLength(); + } else if(source == addLinkButton) { + addLinkActionPerformed(); + refreshSiteLinksList(); + } } public void focusGained(FocusEvent e) { } public void focusLost(FocusEvent e) { + Object source = e.getSource(); + if (source == siteXField || source == siteYField || source == siteZField) { + changePosition(); + } else if(source == linkLengthField) { + // TODO: do NOT call here changeLinkLength(), it will modified the newly selected link instead the old one + // changeLinkLength(); + } } - public void propertyChange(java.beans.PropertyChangeEvent event) { - + public void propertyChange(java.beans.PropertyChangeEvent e) { + if(e.getSource() instanceof Model && e.getPropertyName().equals(RbmModelContainer.PROPERTY_NAME_MOLECULAR_TYPE_LIST)) { + updateInterface(); + } + // TODO: I think this is not needed +// if (e.getSource() == selectionManager) { +// System.out.println(e.getPropertyName()); +// if (e.getPropertyName().equals(SelectionManager.PROPERTY_NAME_SELECTED_OBJECTS)) { +// Object[] objects = selectionManager.getSelectedObjects(); +// onSelectedObjectsChange(objects); +// } else if (e.getPropertyName().equals(SelectionManager.PROPERTY_NAME_ACTIVE_VIEW)) { +// onActiveViewChange(selectionManager.getActiveView()); +// } +// } + } + public void valueChanged(javax.swing.event.ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; + } + if (e.getSource() == getSpeciesContextSpecsTable().getSelectionModel()) { + setSelectedObjectsFromTable(getSpeciesContextSpecsTable(), speciesContextSpecsTableModel); + int row = getSpeciesContextSpecsTable().getSelectedRow(); + SpeciesContextSpec scsSelected = speciesContextSpecsTableModel.getValueAt(row); + setSpeciesContextSpec(scsSelected); + } + if (e.getSource() == getMolecularTypeSpecsTable().getSelectionModel()) { + int row = getMolecularTypeSpecsTable().getSelectedRow(); + MolecularComponentPattern mcmSelected = molecularTypeSpecsTableModel.getValueAt(row); + setMolecularComponentPattern(mcmSelected); + } + if(e.getSource() == siteLinksList) { + showLinkLength(siteLinksList.getSelectedValue()); + } } } @@ -63,35 +204,77 @@ public MolecularStructuresPanel() { @Override public ActiveViewID getActiveView() { - return ActiveViewID.network_setting; + return ActiveViewID.molecular_structure_setting; } - /** - * no-op - */ + @Override public void setSearchText(String s) { } + private void initConnections() throws java.lang.Exception { // listeners here! + siteXField.addFocusListener(eventHandler); + siteYField.addFocusListener(eventHandler); + siteZField.addFocusListener(eventHandler); + linkLengthField.addFocusListener(eventHandler); + siteXField.addActionListener(eventHandler); + siteYField.addActionListener(eventHandler); + siteZField.addActionListener(eventHandler); + + siteLinksList.addListSelectionListener(eventHandler); + siteLinksList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - private void initialize() { + linkLengthField.addActionListener(eventHandler); + addLinkButton.addActionListener(eventHandler); + + ListSelectionModel lsm = getSpeciesContextSpecsTable().getSelectionModel(); + if(lsm instanceof DefaultListSelectionModel) { + DefaultListSelectionModel dlsm = (DefaultListSelectionModel)lsm; + dlsm.addListSelectionListener(eventHandler); + } + + ListSelectionModel lsm2 = getMolecularTypeSpecsTable().getSelectionModel(); + if(lsm2 instanceof DefaultListSelectionModel) { + DefaultListSelectionModel dlsm = (DefaultListSelectionModel)lsm2; + dlsm.addListSelectionListener(eventHandler); + } + + } + + private void initialize() { + try { + // labels / button / combos / lists initialization + siteXField = new JTextField(""); + siteYField = new JTextField(""); + siteZField = new JTextField(""); + siteColorComboBox = new JComboBox<>(); // TODO: arg here should be combobox model + siteLinksList = new JList(siteLinksListModel); + siteLinksList.setCellRenderer(siteLinksCellRenderer); + linkLengthField = new JTextField(""); + addLinkButton = new JButton("Add Link"); + siteXField.setEditable(false); + siteYField.setEditable(false); + siteZField.setEditable(false); + linkLengthField.setEditable(false); + + // ------------------------------------------- The 2 group boxes -------------------------- JPanel thePanel = new JPanel(); Border loweredEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); Border loweredBevelBorder = BorderFactory.createLoweredBevelBorder(); - TitledBorder titleLeft = BorderFactory.createTitledBorder(loweredEtchedBorder, " Molecule Types "); - titleLeft.setTitleJustification(TitledBorder.LEFT); - titleLeft.setTitlePosition(TitledBorder.TOP); + TitledBorder titleTop = BorderFactory.createTitledBorder(loweredEtchedBorder, " Species "); + titleTop.setTitleJustification(TitledBorder.LEFT); + titleTop.setTitlePosition(TitledBorder.TOP); - TitledBorder titleCenter = BorderFactory.createTitledBorder(loweredEtchedBorder, " Sites "); - titleCenter.setTitleJustification(TitledBorder.LEFT); - titleCenter.setTitlePosition(TitledBorder.TOP); - - TitledBorder titleRight = BorderFactory.createTitledBorder(loweredEtchedBorder, " Links "); - titleRight.setTitleJustification(TitledBorder.LEFT); - titleRight.setTitlePosition(TitledBorder.TOP); + TitledBorder titleSites = BorderFactory.createTitledBorder(loweredEtchedBorder, " Sites "); + titleSites.setTitleJustification(TitledBorder.LEFT); + titleSites.setTitlePosition(TitledBorder.TOP); + + TitledBorder titleLinks = BorderFactory.createTitledBorder(loweredEtchedBorder, " Links "); + titleLinks.setTitleJustification(TitledBorder.LEFT); + titleLinks.setTitlePosition(TitledBorder.TOP); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -102,323 +285,325 @@ private void initialize() { gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(1, 1, 1, 1); add(thePanel, gbc); + + // ------------------------------------------- Populating the top group box --------------- + JPanel top = new JPanel(); + JPanel bottom = new JPanel(); + JPanel sitesPanel = new JPanel(); + JPanel linksPanel = new JPanel(); - // ------------------------------------------- the 3 main panels --------------- - JPanel left = new JPanel(); - JPanel center = new JPanel(); - JPanel right = new JPanel(); - - left.setBorder(titleLeft); - center.setBorder(titleCenter); - right.setBorder(titleRight); + top.setBorder(titleTop); + sitesPanel.setBorder(titleSites); + linksPanel.setBorder(titleLinks); thePanel.setLayout(new GridBagLayout()); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1.0; - gbc.weighty = 1.0; + gbc.weighty = 0.8; gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); // top, left, bottom, right - thePanel.add(left, gbc); + gbc.insets = new Insets(3, 0, 0, 0); // top, left, bottom, right + thePanel.add(top, gbc); gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 0; + gbc.gridx = 0; + gbc.gridy = 1; gbc.weightx = 1.0; gbc.weighty = 1.0; + gbc.gridheight = 2; + gbc.insets = new Insets(1, 0, 0, 0); gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - thePanel.add(center, gbc); + thePanel.add(bottom, gbc); - gbc = new GridBagConstraints(); - gbc.gridx = 2; - gbc.gridy = 0; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - thePanel.add(right, gbc); - - // --- left ------------------------------------------------- - JPanel leftSubpanel1 = new JPanel(); - JList molecules = new JList (); - molecules.setBorder(loweredEtchedBorder); - JPanel leftSubpanel2 = new JPanel(); - JTextField radius = new JTextField(); - JTextField diameter = new JTextField(); - JTextField color = new JTextField(); - - leftSubpanel1.setLayout(new GridBagLayout()); + // we may want to use a scroll pane whose viewing area is the JTable to provide similar look with NetGen Console + JScrollPane pt = new JScrollPane(getSpeciesContextSpecsTable()); + pt.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + top.setLayout(new GridBagLayout()); // --- table of top panel gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel1.add(molecules, gbc); - - leftSubpanel2.setLayout(new GridBagLayout()); - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(new JLabel("Radius (nm): "), gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 0; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); // top, left, bottom, right - leftSubpanel2.add(radius, gbc); + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(2, 3, 3, 4); + top.add(pt, gbc); + + DefaultTableCellRenderer renderer = new DefaultScrollTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setIcon(null); + defaultToolTipText = null; + + if (value instanceof Species) { + setText(((Species)value).getCommonName()); + defaultToolTipText = getText(); + setToolTipText(defaultToolTipText); + } else if (value instanceof SpeciesContext) { + setText(((SpeciesContext)value).getName()); + defaultToolTipText = getText(); + setToolTipText(defaultToolTipText); + } else if (value instanceof Structure) { + setText(((Structure)value).getName()); + defaultToolTipText = getText(); + setToolTipText(defaultToolTipText); + } else if (value instanceof ScopedExpression) { + SpeciesContextSpec scSpec = speciesContextSpecsTableModel.getValueAt(row); + VCUnitDefinition unit = null; + if (table.getColumnName(column).equals(SpeciesContextSpecsTableModel.ColumnType.COLUMN_INITIAL.label)) { + SpeciesContextSpecParameter initialConditionParameter = scSpec.getInitialConditionParameter(); + unit = initialConditionParameter.getUnitDefinition(); + } else if (table.getColumnName(column).equals(SpeciesContextSpecsTableModel.ColumnType.COLUMN_DIFFUSION.label)) { + SpeciesContextSpecParameter diffusionParameter = scSpec.getDiffusionParameter(); + unit = diffusionParameter.getUnitDefinition(); + } + if (unit != null) { + setHorizontalTextPosition(JLabel.LEFT); + setIcon(new TextIcon("[" + unit.getSymbolUnicode() + "]", DefaultScrollTableCellRenderer.uneditableForeground)); + } + int rgb = 0x00ffffff & DefaultScrollTableCellRenderer.uneditableForeground.getRGB(); + defaultToolTipText = "" + StringEscapeUtils.escapeHtml4(getText()) + " [" + unit.getSymbolUnicode() + "] "; + setToolTipText(defaultToolTipText); + if(unit != null) { + setText(defaultToolTipText); + } + } + + TableModel tableModel = table.getModel(); + if (tableModel instanceof SortTableModel) { + DefaultScrollTableCellRenderer.issueRenderer(this, defaultToolTipText, table, row, column, (SortTableModel)tableModel); + setHorizontalTextPosition(JLabel.TRAILING); + } + return this; + } + }; + DefaultTableCellRenderer rbmSpeciesShapeDepictionCellRenderer = new DefaultScrollTableCellRenderer() { + SpeciesPatternSmallShape spss = null; + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (table.getModel() instanceof VCellSortTableModel) { + Object selectedObject = null; + if (table.getModel() == speciesContextSpecsTableModel) { + selectedObject = speciesContextSpecsTableModel.getValueAt(row); + } + if (selectedObject != null) { + if(selectedObject instanceof SpeciesContextSpec) { + SpeciesContextSpec scs = (SpeciesContextSpec)selectedObject; + SpeciesContext sc = scs.getSpeciesContext(); + SpeciesPattern sp = sc.getSpeciesPattern(); // sp may be null for "plain" species contexts + Graphics panelContext = table.getGraphics(); + spss = new SpeciesPatternSmallShape(4, 2, sp, shapeManager, panelContext, sc, isSelected, issueManager); + } + } else { + spss = null; + } + } + setText(""); + return this; + } + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if(spss != null) { + spss.paintSelf(g); + } + } + }; - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 1; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(new JLabel("D (um^2/s): "), gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 1; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(diameter, gbc); - - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 2; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(new JLabel("Color: "), gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 2; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(color, gbc); + // renderer for the rules column (reaction rules / assignment rules) + // TODO: rules not compatible with springsalad, must create issue if present + DefaultScrollTableCellRenderer rulesTableCellRenderer = new DefaultScrollTableCellRenderer() { + final Color lightBlueBackground = new Color(214, 234, 248); + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if (table.getModel() instanceof SpeciesContextSpecsTableModel) { + Icon icon = VCellIcons.issueGoodIcon; + Object selectedObject = null; + if (table.getModel() == speciesContextSpecsTableModel) { + selectedObject = speciesContextSpecsTableModel.getValueAt(row); + } + if (selectedObject != null) { + if(isSelected) { + setBackground(lightBlueBackground); + } + if(selectedObject instanceof SpeciesContextSpec) { + SpeciesContextSpec scs = (SpeciesContextSpec)selectedObject; + SpeciesContext sc = scs.getSpeciesContext(); + + boolean foundRuleMatch = false; + if(fieldSimulationContext.getRateRules() != null && fieldSimulationContext.getRateRules().length > 0) { + for(RateRule rr : fieldSimulationContext.getRateRules()) { + if(rr.getRateRuleVar() == null) { + continue; + } + if(sc.getName().equals(rr.getRateRuleVar().getName())) { + foundRuleMatch = true; + icon = VCellIcons.ruleRateIcon; + break; + } + } + } + if(!foundRuleMatch && fieldSimulationContext.getAssignmentRules() != null && fieldSimulationContext.getAssignmentRules().length > 0) { + for(AssignmentRule rr : fieldSimulationContext.getAssignmentRules()) { + if(rr.getAssignmentRuleVar() == null) { + continue; + } + if(sc.getName().equals(rr.getAssignmentRuleVar().getName())) { + icon = VCellIcons.ruleAssignIcon; + break; + } + } + } + } + } + setIcon(icon); + } + return this; + } + }; - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(new JLabel("States"), gbc); - - JList states = new JList (); - states.setBorder(loweredEtchedBorder); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 4; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.gridwidth = 2; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - leftSubpanel2.add(states, gbc); - - left.setLayout(new GridBagLayout()); + // The Structures combobox cell renderer in the MolecularTypeSpecsTable + DefaultScrollTableCellRenderer structuresTableCellRenderer = new DefaultScrollTableCellRenderer() { + final Color lightBlueBackground = new Color(214, 234, 248); + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if (table.getModel() instanceof MolecularTypeSpecsTableModel) { + MolecularTypeSpecsTableModel molecularTypeSpecsTableModel = (MolecularTypeSpecsTableModel)table.getModel(); + if (value instanceof Structure) { + Structure structure = (Structure)value; + setText(structure.getName()); + } else { + if(value != null) { + setText(value.toString()); + } + } + } + return this; + } + }; + + getSpeciesContextSpecsTable().setDefaultRenderer(SpeciesContext.class, renderer); + getSpeciesContextSpecsTable().setDefaultRenderer(Structure.class, renderer); + getSpeciesContextSpecsTable().setDefaultRenderer(SpeciesPattern.class, rbmSpeciesShapeDepictionCellRenderer); // depiction icons + getSpeciesContextSpecsTable().setDefaultRenderer(Species.class, renderer); + getSpeciesContextSpecsTable().setDefaultRenderer(ScopedExpression.class, renderer); + getSpeciesContextSpecsTable().setDefaultRenderer(Boolean.class, new ScrollTableBooleanCellRenderer()); + getSpeciesContextSpecsTable().setDefaultRenderer(SpeciesContextSpecsTableModel.RulesProvenance.class, rulesTableCellRenderer); // icons for assignment and rate rules + + // --------------------------------------------------------------------------------------------- + + bottom.setLayout(new GridBagLayout()); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1.0; gbc.weighty = 1.0; + gbc.gridwidth = 3; gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - left.add(leftSubpanel1, gbc); + gbc.insets = new Insets(3, 2, 2, 3); // top, left, bottom, right + bottom.add(sitesPanel, gbc); gbc = new GridBagConstraints(); - gbc.gridx = 1; + gbc.gridx = 3; gbc.gridy = 0; - gbc.weightx = 1.0; + gbc.weightx = 0.5; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - left.add(leftSubpanel2, gbc); - - // --- center -------------------------------------------------- - JPanel centerSubpanel1 = new JPanel(); - JList sites = new JList (); - sites.setBorder(loweredEtchedBorder); - JPanel centerSubpanel2 = new JPanel(); - JTextField radiusCenter = new JTextField(); - JTextField diameterCenter = new JTextField(); - JTextField locationCenter = new JTextField(); - - centerSubpanel1.setLayout(new GridBagLayout()); + gbc.insets = new Insets(3, 2, 2, 3); + bottom.add(linksPanel, gbc); + + JScrollPane pb = new JScrollPane(getMolecularTypeSpecsTable()); + pb.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + sitesPanel.setLayout(new GridBagLayout()); // --- table of bottom panel gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1.0; gbc.weighty = 1.0; + gbc.gridwidth = 8; + gbc.gridheight = 5; gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel1.add(sites, gbc); - - centerSubpanel2.setLayout(new GridBagLayout()); - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(new JLabel("Radius (nm): "), gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 0; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); // top, left, bottom, right - centerSubpanel2.add(radiusCenter, gbc); - - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 1; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(new JLabel("D (um^2/s): "), gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 1; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(diameterCenter, gbc); - - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 2; - gbc.anchor = GridBagConstraints.EAST; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(new JLabel("Location: "), gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 2; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(locationCenter, gbc); - - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(new JLabel("Position (nm) "), gbc); + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.insets = new Insets(2, 3, 3, 4); + sitesPanel.add(pb, gbc); - JPanel centerSubpanel3 = new JPanel(); // =================== gbc = new GridBagConstraints(); gbc.gridx = 0; - gbc.gridy = 4; - gbc.gridwidth = 2; - gbc.weightx = 1.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(2, 2, 2, 3); - centerSubpanel2.add(centerSubpanel3, gbc); - - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; gbc.gridy = 5; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(new JButton("Set Position"), gbc); - - gbc = new GridBagConstraints(); // ghost - gbc.gridx = 0; - gbc.gridy = 6; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.VERTICAL; - gbc.insets = new Insets(5, 2, 2, 3); - centerSubpanel2.add(new JLabel(" "), gbc); - - - centerSubpanel3.setLayout(new GridBagLayout()); - gbc = new GridBagConstraints(); // ---------------------- - gbc.gridx = 0; - gbc.gridy = 0; - gbc.insets = new Insets(0, 2, 2, 0); // top, left, bottom, right - centerSubpanel3.add(new JLabel("X: "), gbc); + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(new JLabel(" X: "), gbc); - JTextField x = new JTextField(); - x.setEditable(false); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 0; + gbc.gridy = 5; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 1, 2, 3); - centerSubpanel3.add(x, gbc); + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(siteXField, gbc); // - gbc = new GridBagConstraints(); // ---------------------- + gbc = new GridBagConstraints(); gbc.gridx = 2; - gbc.gridy = 0; - gbc.insets = new Insets(0, 7, 2, 0); - centerSubpanel3.add(new JLabel("Y: "), gbc); + gbc.gridy = 5; + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(new JLabel(" Y: "), gbc); - JTextField y = new JTextField(); - y.setEditable(false); gbc = new GridBagConstraints(); gbc.gridx = 3; - gbc.gridy = 0; + gbc.gridy = 5; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 1, 2, 3); - centerSubpanel3.add(y, gbc); + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(siteYField, gbc); - gbc = new GridBagConstraints(); // ---------------------- + gbc = new GridBagConstraints(); gbc.gridx = 4; - gbc.gridy = 0; - gbc.insets = new Insets(0, 7, 2, 0); - centerSubpanel3.add(new JLabel("Z: "), gbc); + gbc.gridy = 5; + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(new JLabel(" Z: "), gbc); - JTextField z = new JTextField(); - z.setEditable(false); gbc = new GridBagConstraints(); gbc.gridx = 5; - gbc.gridy = 0; + gbc.gridy = 5; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(0, 1, 2, 0); - centerSubpanel3.add(z, gbc); + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(siteZField, gbc); - center.setLayout(new GridBagLayout()); gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - center.add(centerSubpanel1, gbc); - + gbc.gridx = 6; + gbc.gridy = 5; + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(new JLabel(" Color "), gbc); + gbc = new GridBagConstraints(); - gbc.gridx = 1; - gbc.gridy = 0; + gbc.gridx = 7; + gbc.gridy = 5; gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(5, 2, 2, 3); - center.add(centerSubpanel2, gbc); - - // --- right ----------------------------------------------- - JList links = new JList (); - links.setBorder(loweredEtchedBorder); - JTextField lengthRight = new JTextField(); - lengthRight.setEditable(false); - JButton addLink = new JButton("Add Link"); - JButton removeLink = new JButton("Remove Link"); - JButton setLinkLength = new JButton("Set Link Length"); - - right.setLayout(new GridBagLayout()); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.SOUTH; + gbc.insets = new Insets(2, 2, 2, 2); + sitesPanel.add(siteColorComboBox, gbc); + +// // --- links ----------------------------------------------- + linksPanel.setLayout(new GridBagLayout()); + JScrollPane scrollPane1 = new JScrollPane(siteLinksList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; @@ -427,14 +612,14 @@ private void initialize() { gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(5, 2, 2, 3); - right.add(links, gbc); + linksPanel.add(scrollPane1, gbc); gbc = new GridBagConstraints(); // ---------------------- gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; gbc.insets = new Insets(5, 2, 2, 3); - right.add(new JLabel("Length (nm): "), gbc); + linksPanel.add(new JLabel("Length (nm): "), gbc); gbc = new GridBagConstraints(); gbc.gridx = 1; @@ -442,29 +627,61 @@ private void initialize() { gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(5, 2, 2, 3); // top, left, bottom, right - right.add(lengthRight, gbc); + linksPanel.add(linkLengthField, gbc); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(5, 2, 2, 3); - right.add(addLink, gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 3; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - right.add(removeLink, gbc); - - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 4; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 2, 2, 3); - right.add(setLinkLength, gbc); + linksPanel.add(addLinkButton, gbc); + + getMolecularTypeSpecsTable().setDefaultRenderer(String.class, new DefaultScrollTableCellRenderer()); + getMolecularTypeSpecsTable().setDefaultRenderer(Structure.class, structuresTableCellRenderer); // The Structures combobox cell renderer + + + initConnections(); // adding listeners + + } catch(Throwable e) { + handleException(e); + } + } + + private EditorScrollTable getMolecularTypeSpecsTable() { + if (molecularTypeSpecsTable == null) { + try { + molecularTypeSpecsTable = new EditorScrollTable(); + molecularTypeSpecsTable.setName("molecularComponentSpecsTable"); + molecularTypeSpecsTableModel = new MolecularTypeSpecsTableModel(molecularTypeSpecsTable); + molecularTypeSpecsTable.setModel(molecularTypeSpecsTableModel); +// molecularComponentSpecsTable.setScrollTableActionManager(new InternalScrollTableActionManager(table)); + molecularTypeSpecsTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return molecularTypeSpecsTable; + } + private EditorScrollTable getSpeciesContextSpecsTable() { + if (speciesContextSpecsTable == null) { + try { + speciesContextSpecsTable = new EditorScrollTable(); + speciesContextSpecsTable.setName("spceciesContextSpecsTable"); + speciesContextSpecsTableModel = new SpeciesContextSpecsTableModel(speciesContextSpecsTable); + speciesContextSpecsTableModel.setEditable(false); + speciesContextSpecsTable.setModel(speciesContextSpecsTableModel); +// speciesContextSpecsTable.setScrollTableActionManager(new InternalScrollTableActionManager(speciesContextSpecsTable)); + speciesContextSpecsTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); + } catch (java.lang.Throwable ivjExc) { + handleException(ivjExc); + } + } + return speciesContextSpecsTable; + } + private void handleException(Throwable exception) { + System.out.println("--------- UNCAUGHT EXCEPTION --------- in cbit.vcell.mapping.InitialConditionPanel"); + exception.printStackTrace(System.out); } public void setSimulationContext(SimulationContext simulationContext) { @@ -482,43 +699,161 @@ public void setSimulationContext(SimulationContext simulationContext) { m.removePropertyChangeListener(eventHandler); m.addPropertyChangeListener(eventHandler); } - - refreshInterface(); + speciesContextSpecsTableModel.setSimulationContext(simulationContext); + molecularTypeSpecsTableModel.setSimulationContext(simulationContext); + updateInterface(); } public SimulationContext getSimulationContext() { return fieldSimulationContext; } - - public void setSelectionManager(SelectionManager selectionManager) { - fieldSelectionManager = selectionManager; + + void setSpeciesContextSpec(SpeciesContextSpec newValue) { + if (fieldSpeciesContextSpec == newValue) { + return; + } + SpeciesContextSpec oldValue = fieldSpeciesContextSpec; + if (oldValue != null) { + oldValue.removePropertyChangeListener(eventHandler); + } + // commit the changes before switch to another species + changeSpeciesContextSpec(); + + fieldSpeciesContextSpec = newValue; + if (newValue != null) { + newValue.addPropertyChangeListener(eventHandler); + } + molecularTypeSpecsTableModel.setSpeciesContextSpec(fieldSpeciesContextSpec); + updateInterface(); } - public IssueManager getIssueManager() { - return fieldIssueManager; + public SpeciesContextSpec getSpeciesContextSpec() { + return fieldSpeciesContextSpec; } - public void setIssueManager(IssueManager issueManager) { - fieldIssueManager = issueManager; + void setMolecularComponentPattern(MolecularComponentPattern mcp) { + fieldMolecularComponentPattern = mcp; + //TODO: stuff + updateInterface(); } + + // ============================================================================================ - - - private void appendToConsole(String string) { - TaskCallbackMessage newCallbackMessage = new TaskCallbackMessage(TaskCallbackStatus.Error, string); - fieldSimulationContext.appendToConsole(newCallbackMessage); - } - private void appendToConsole(TaskCallbackMessage newCallbackMessage) { - fieldSimulationContext.appendToConsole(newCallbackMessage); + public void setIssueManager(IssueManager issueManager) { + super.setIssueManager(issueManager); + speciesContextSpecsTableModel.setIssueManager(issueManager); } - - public void refreshInterface() { - + private void updateInterface() { + boolean bNonNullSpeciesContextSpec = fieldSpeciesContextSpec != null && fieldSimulationContext != null; + boolean bNonNullSpeciesPattern = bNonNullSpeciesContextSpec && fieldSpeciesContextSpec.getSpeciesContext().getSpeciesPattern() != null; + boolean bNonNullMolecularTypePattern = bNonNullSpeciesPattern && fieldSpeciesContextSpec.getSpeciesContext().getSpeciesPattern().getMolecularTypePatterns().get(0) != null; + boolean bNonNullMolecularComponentPattern = bNonNullMolecularTypePattern && fieldMolecularComponentPattern != null; + + MolecularTypePattern mtp = null; + if(bNonNullMolecularTypePattern) { + mtp = fieldSpeciesContextSpec.getSpeciesContext().getSpeciesPattern().getMolecularTypePatterns().get(0); + } + + if(bNonNullMolecularTypePattern && mtp.getComponentPatternList().size() > 1) { // a link requires 2 sites (components) + addLinkButton.setEnabled(true); + refreshSiteLinksList(); + } else { + linkLengthField.setEditable(false); + linkLengthField.setText(null); + addLinkButton.setEnabled(false); + refreshSiteLinksList(); + } + if(bNonNullMolecularComponentPattern) { + SiteAttributesSpec sas = fieldSpeciesContextSpec.getSiteAttributesMap().get(fieldMolecularComponentPattern); + siteXField.setEditable(true); + siteYField.setEditable(true); + siteZField.setEditable(true); + siteXField.setText(sas.getCoordinate().getX()+""); + siteYField.setText(sas.getCoordinate().getY()+""); + siteZField.setText(sas.getCoordinate().getZ()+""); + } else { + siteXField.setEditable(false); + siteYField.setEditable(false); + siteZField.setEditable(false); + siteXField.setText(null); + siteYField.setText(null); + siteZField.setText(null); + } } + private void refreshSiteLinksList() { + siteLinksListModel.removeAllElements(); + if(fieldSpeciesContextSpec != null) { + for (MolecularInternalLinkSpec mils : fieldSpeciesContextSpec.getInternalLinkSet()){ + siteLinksListModel.addElement(mils); + } + } + } @Override protected void onSelectedObjectsChange(Object[] selectedObjects) { + setTableSelections(selectedObjects, speciesContextSpecsTable, speciesContextSpecsTableModel); + } + /* + * TODO: commit changes to current (old) SpeciesContextStep + * before the newly selected SpeciesContextStep becomes current + */ + private void changeSpeciesContextSpec() { + + } + + private void changePosition() { + System.out.println("Site coordinates changed"); + // TODO: save X,Y,Z in coordinate object + recalculateLinkLengths(); + } + private void changeLinkLength() { + Double linkLength = Double.parseDouble(linkLengthField.getText()); + MolecularInternalLinkSpec selectedValue = siteLinksList.getSelectedValue(); + int selectedIndex = siteLinksList.getSelectedIndex(); + System.out.println("changeLinkLength(): selected index '" + selectedIndex + "' being set to " + linkLength); + // TODO: set x,y,z for sites, link length will be always calculated from xyz +// selectedValue.setLinkLength(linkLength); + recalculatePositions(); + } + + private void recalculateLinkLengths() { + } + private void recalculatePositions() { } + private void addLinkActionPerformed() { + AddLinkPanel panel = new AddLinkPanel(this); + ChildWindowManager childWindowManager = ChildWindowManager.findChildWindowManager(this); + ChildWindow childWindow = childWindowManager.addChildWindow(panel, panel, " Add Link "); + Dimension dim = new Dimension(320, 330); + childWindow.pack(); + panel.setChildWindow(childWindow); + childWindow.setPreferredSize(dim); + childWindow.showModal(); + + if(panel.getButtonPushed() == AddLinkPanel.ActionButtons.Apply) { + MolecularComponentPattern firstMcp = panel.getFirstSiteList().getSelectedValue(); + MolecularComponentPattern secondMcp = panel.getSecondSiteList().getSelectedValue(); + MolecularInternalLinkSpec mils = new MolecularInternalLinkSpec(fieldSpeciesContextSpec, firstMcp, secondMcp); + fieldSpeciesContextSpec.getInternalLinkSet().add(mils); + return; + } else { + + return; + } + } + + private void showLinkLength(MolecularInternalLinkSpec selectedValue) { + if(selectedValue == null) { + System.out.println("showLinkLength: SelectedValue is null"); + linkLengthField.setEditable(false); + linkLengthField.setText(null); + return; // nothing selected + } + System.out.println("showLinkLength(): Selected row is '" + siteLinksList.getSelectedIndex() + "'"); + linkLengthField.setEditable(true); + linkLengthField.setText(selectedValue.getLinkLength()+""); + }; + } diff --git a/vcell-client/src/main/java/org/vcell/solver/nfsim/gui/NFSimSimulationOptionsPanel.java b/vcell-client/src/main/java/org/vcell/solver/nfsim/gui/NFSimSimulationOptionsPanel.java index 21d8805cf1..22ad114a8f 100644 --- a/vcell-client/src/main/java/org/vcell/solver/nfsim/gui/NFSimSimulationOptionsPanel.java +++ b/vcell-client/src/main/java/org/vcell/solver/nfsim/gui/NFSimSimulationOptionsPanel.java @@ -577,6 +577,10 @@ private void refresh() { setVisible(false); return; } + if(mathDescription.isLangevin()) { + setVisible(false); + return; + } } setVisible(true); NFsimSimulationOptions nfsimSimulationOptions = solverTaskDescription.getNFSimSimulationOptions(); diff --git a/vcell-client/src/main/java/org/vcell/util/gui/DefaultScrollTableCellRenderer.java b/vcell-client/src/main/java/org/vcell/util/gui/DefaultScrollTableCellRenderer.java index d7bd81aef7..ee6211a424 100644 --- a/vcell-client/src/main/java/org/vcell/util/gui/DefaultScrollTableCellRenderer.java +++ b/vcell-client/src/main/java/org/vcell/util/gui/DefaultScrollTableCellRenderer.java @@ -38,6 +38,7 @@ import cbit.vcell.client.desktop.biomodel.SpatialObjectTableModel; import cbit.vcell.client.desktop.biomodel.SpatialProcessTableModel; import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.gui.NetworkConstraintsTableModel; import cbit.vcell.mapping.spatial.PointObject; import cbit.vcell.mapping.spatial.SpatialObject; @@ -163,6 +164,9 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole icon = VCellIcons.appStoSpatialIcon; toolTipSuffix = "Stochastic / Spatial"; } + } else if(simContext.getApplicationType() == Application.SPRINGSALAD) { + icon = VCellIcons.appSpringSaLaDSpatialIcon; + toolTipSuffix = "SpringSaLaD / Spatial"; } else { // deterministic if(simContext.getGeometry().getDimension() == 0) { icon = VCellIcons.appDetNonspIcon; @@ -260,6 +264,8 @@ public static String nicelyFormattedDouble(Double doubleValue) { } public static void issueRenderer(JLabel renderer, String defaultToolTipText, JTable table, int row, int column, SortTableModel tableModel) { + // if the following lists are empty (when they shouldn't), check in tableModel.getIssues() + // if the instanceof the class of the IssueSource is properly filtered in List issueListError = tableModel.getIssues(row, column, Issue.Severity.ERROR); List issueListWarning = tableModel.getIssues(row, column, Issue.Severity.WARNING); Icon icon = null; diff --git a/vcell-client/src/main/java/org/vcell/util/gui/exporter/FileFilters.java b/vcell-client/src/main/java/org/vcell/util/gui/exporter/FileFilters.java index 97882ce44d..5b182ecce3 100644 --- a/vcell-client/src/main/java/org/vcell/util/gui/exporter/FileFilters.java +++ b/vcell-client/src/main/java/org/vcell/util/gui/exporter/FileFilters.java @@ -79,6 +79,7 @@ public static List supports(SelectorExtensionFilter.Selector ... sel public static final FileFilter FILE_FILTER_SBML_32_SPATIAL = new SbmlExtensionFilter(3, 2, true); public static final FileFilter FILE_FILTER_SEDML = new SedmlExtensionFilter(); public static final FileFilter FILE_FILTER_OMEX = new OmexExtensionFilter(); + public static final FileFilter FILE_FILTER_SPRINGSALAD = new SpringSaLaDExtensionFilter(); public static final FileFilter FILE_FILTER_CELLML = new CellMLExtensionFilter(); public static final FileFilter FILE_FILTER_SMOLDYN_INPUT = new SmoldynExtensionFilter(); public static final FileFilter FILE_FILTER_NFSIM = new NfsimExtensionFilter(); diff --git a/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java b/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java new file mode 100644 index 0000000000..f6cd7a1011 --- /dev/null +++ b/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java @@ -0,0 +1,110 @@ +package org.vcell.util.gui.exporter; + +import java.awt.Component; +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +import org.vcell.sedml.ModelFormat; +import org.vcell.sedml.SEDMLExporter; +import org.vcell.util.FileUtils; +import org.vcell.util.UserCancelException; +import org.vcell.util.gui.DialogUtils; +import org.vcell.util.gui.exporter.ExtensionFilter.ChooseContext; + +import cbit.util.xml.XmlUtil; +import cbit.vcell.biomodel.BioModel; +import cbit.vcell.client.PopupGenerator; +import cbit.vcell.clientdb.DocumentManager; +import cbit.vcell.export.SpringSaLaDExporter; +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; + +@SuppressWarnings("serial") +public class SpringSaLaDExtensionFilter extends SelectorExtensionFilter { + private static final String FNAMES = ".ssld"; + private Component parentComponent = null; + protected ModelFormat modelFormat = ModelFormat.SPRINGSALAD; + private SimulationContext simContext = null; + + public SpringSaLaDExtensionFilter() { + this(FNAMES, "SpringSaLaD format (.ssld)", SelectorExtensionFilter.Selector.FULL_MODEL); + } + public SpringSaLaDExtensionFilter(String fNames, String name, Selector selector) { + super(fNames, name, selector); + } + + @Override + public void askUser(ChooseContext c) throws UserCancelException { + if(c.chosenContext == null) { + return; + } + BioModel bioModel = c.chosenContext.getBioModel(); + parentComponent = c.currentWindow; // will need it later, hack to center another window in ssldExporter.writeDocumentStringToFile() + LinkedHashMap springSaLaDApplications = new LinkedHashMap<> (); + if(bioModel != null) { + for(SimulationContext candidate : bioModel.getSimulationContexts()) { + if(candidate.getApplicationType() == Application.SPRINGSALAD) { + springSaLaDApplications.put(candidate.getName(), candidate); + } + } + if(springSaLaDApplications.size() == 0) { + ; // do nothing, simContext is null + } else if(springSaLaDApplications.size() == 1) { + Entry entry = springSaLaDApplications.entrySet().iterator().next(); + simContext = entry.getValue(); + } else { + Object[] namesArray = springSaLaDApplications.keySet().toArray(); + Object choice = DialogUtils.showListDialog(c.currentWindow, namesArray, "Please select Application to export"); + if(choice == null) + { + throw UserCancelException.CANCEL_FILE_SELECTION; + } + simContext = springSaLaDApplications.get((String)choice); + } + } + System.out.println(simContext); + } + + @Override + public void writeBioModel(DocumentManager documentManager, BioModel bioModel, File exportFile, SimulationContext ignored) throws Exception { + String resultString; + // export the entire biomodel to a SEDML file (for now, only non-spatial,non-stochastic applns) + int ssldMajor = 2; + int ssldMinor = 2; + String sPath = FileUtils.getFullPathNoEndSeparator(exportFile.getAbsolutePath()); + String sFile = FileUtils.getBaseName(exportFile.getAbsolutePath()); + String sExt = FileUtils.getExtension(exportFile.getAbsolutePath()); + + SpringSaLaDExporter ssldExporter = null; + if (bioModel == null) { + throw new RuntimeException("unsupported Document Type " + Objects.requireNonNull(bioModel).getClass().getName() + " for SpringSaLaD export"); + } + if(simContext == null) { + throw new RuntimeException("SpringSaLaD application required"); + } + ssldExporter = new SpringSaLaDExporter(sFile, ssldMajor, ssldMinor); + resultString = ssldExporter.getDocumentAsString(bioModel, simContext); + doSpecificWork(ssldExporter, resultString, sPath, sFile); + return; + } + + public void doSpecificWork(SpringSaLaDExporter ssldExporter, String resultString, String sPath, String sFile) throws Exception { + ssldExporter.writeDocumentStringToFile(resultString, sPath, sFile, parentComponent); +// sedmlExporter.addSedmlFileToList(sFile + ".sedml"); + } + + @Override + public boolean requiresMoreChoices() { //tells ChooseFile to call askUser() above + return true; + } + +} diff --git a/vcell-core/pom.xml b/vcell-core/pom.xml index 47361c017c..69daf6580a 100644 --- a/vcell-core/pom.xml +++ b/vcell-core/pom.xml @@ -458,7 +458,7 @@ wget - https://github.com/virtualcell/vcell-solvers/releases/download/v0.0.40/mac64.tgz + https://github.com/virtualcell/vcell-solvers/releases/download/v0.0.43/mac64.tgz true ${project.build.directory}/../../localsolvers/mac64 @@ -538,7 +538,7 @@ wget - https://github.com/virtualcell/vcell-solvers/releases/download/v0.0.40/mac64.tgz + https://github.com/virtualcell/vcell-solvers/releases/download/v0.0.43/mac64.tgz true ${project.build.directory}/../../localsolvers/mac64 diff --git a/vcell-core/src/main/java/cbit/vcell/biomodel/BioModel.java b/vcell-core/src/main/java/cbit/vcell/biomodel/BioModel.java index 2361ca46c1..54c92d8a39 100644 --- a/vcell-core/src/main/java/cbit/vcell/biomodel/BioModel.java +++ b/vcell-core/src/main/java/cbit/vcell/biomodel/BioModel.java @@ -20,6 +20,7 @@ import cbit.image.VCImage; import cbit.vcell.mapping.*; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.model.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -43,12 +44,21 @@ import org.vcell.util.document.*; import org.vcell.util.document.BioModelChildSummary.MathType; +import cbit.image.ImageException; import cbit.vcell.biomodel.meta.IdentifiableProvider; import cbit.vcell.biomodel.meta.VCID; import cbit.vcell.biomodel.meta.VCMetaData; +import cbit.vcell.geometry.AnalyticSubVolume; import cbit.vcell.geometry.Geometry; +import cbit.vcell.geometry.GeometryException; +import cbit.vcell.geometry.GeometrySpec; +import cbit.vcell.geometry.SubVolume; +import cbit.vcell.geometry.SurfaceClass; +import cbit.vcell.geometry.surface.GeometrySurfaceDescription; import cbit.vcell.math.MathDescription; import cbit.vcell.model.Model.RbmModelContainer; +import cbit.vcell.model.Structure.SpringStructureEnum; +import cbit.vcell.parser.ExpressionException; import cbit.vcell.parser.NameScope; import cbit.vcell.parser.SymbolTableEntry; import cbit.vcell.solver.Simulation; @@ -127,11 +137,77 @@ public void transformLumpedToDistributed() { } } - public SimulationContext addNewSimulationContext(String newSimulationContextName, SimulationContext.Application app) throws java.beans.PropertyVetoException { - SimulationContext simContext = new SimulationContext(getModel(),new Geometry("non-spatial",0), null,null,app); - simContext.setName(newSimulationContextName); - addSimulationContext(simContext); - return simContext; + public SimulationContext addNewSimulationContext(String newSimulationContextName, SimulationContext.Application app) throws java.beans.PropertyVetoException, ExpressionException, GeometryException, ImageException, IllegalMappingException, MappingException { + Geometry geometry; + if(Application.SPRINGSALAD == app) { + boolean compatibleCompartments = true; + if(getModel().getStructures().length != 3) { + compatibleCompartments = false; // springsalad needs exactly 3 compartments + } + for(Structure struct : getModel().getStructures()) { + String name = struct.getName(); + if(!Structure.springStructureSet.contains(name)) { + compatibleCompartments = false; // names are hardcoded in SpringStructureEnum + break; + } + } + if(compatibleCompartments == false) { // just make a minimal geometry, we'll complain elsewhere + geometry = new Geometry("non-spatial", 0); + SimulationContext simContext = new SimulationContext(getModel(), geometry, null, null, app); + simContext.setName(newSimulationContextName); + addSimulationContext(simContext); + return simContext; + } + + geometry = new Geometry("spatial", 3); + GeometrySpec geometrySpec = geometry.getGeometrySpec(); + geometrySpec.setOrigin(new org.vcell.util.Origin(0,0,0)); + geometrySpec.setExtent(new org.vcell.util.Extent(1.0,1.0,1.0)); + geometrySpec.addSubVolume(new AnalyticSubVolume(SpringStructureEnum.Intracellular.columnName, new cbit.vcell.parser.Expression("z<0.9"))); + geometrySpec.addSubVolume(new AnalyticSubVolume(SpringStructureEnum.Extracellular.columnName, new cbit.vcell.parser.Expression("1.0;"))); + cbit.vcell.geometry.surface.GeometrySurfaceUtils.updateGeometricRegions(geometry.getGeometrySurfaceDescription()); + + SimulationContext simContext = new SimulationContext(getModel(), geometry, null, null, app); + simContext.setName(newSimulationContextName); + addSimulationContext(simContext); + + Double charSize = simContext.getCharacteristicSize(); + simContext.setCharacteristicSize(Double.valueOf(charSize.doubleValue()/2.0)); + GeometryContext geoContext = simContext.getGeometryContext(); + Model model = geoContext.getModel(); + cbit.vcell.model.Structure structures[] = model.getStructures(); + for (int i=0;i molecularTypeList = model.getRbmModelContainer().getMolecularTypeList(); + List reactionRuleList = model.getRbmModelContainer().getReactionRuleList(); + + if(simContext == null) { // we just need an application, no simulations are needed + throw new RuntimeException("Exporting to SpringSaLaD file format needs at least one SpringSaLaD Application"); + } + if(simContext.getMathDescription() == null) { + throw new RuntimeException("Math Description not found, regenerate math first."); + + } + // make a fake simulation, when exporting we just need some default simulation properties + Simulation simulation = new Simulation(simContext.getMathDescription(), simContext); + + + Geometry geometry = simContext.getGeometry(); + GeometryContext geometryContext = simContext.getGeometryContext(); + GeometrySpec geometrySpec = geometry.getGeometrySpec(); + ReactionContext reactionContext = simContext.getReactionContext(); + SpeciesContextSpec[] speciesContextSpecs = reactionContext.getSpeciesContextSpecs(); + ReactionSpec[] reactionSpecs = reactionContext.getReactionSpecs(); + ReactionRuleSpec[] reactionRuleSpecs = reactionContext.getReactionRuleSpecs(); + + Map creationMap = new LinkedHashMap<> (); + Map decayMap = new LinkedHashMap<> (); + Map> transitionMap = new LinkedHashMap<> (); + Map> allostericMap = new LinkedHashMap<> (); + Map> bindingMap = new LinkedHashMap<> (); + Map> moleculeCreationDecayRates = new LinkedHashMap<> (); + try { + for(ReactionRuleSpec rrs : reactionRuleSpecs) { + if(rrs.isExcluded()) { + continue; + } + Map analysisResults = new LinkedHashMap<> (); + rrs.analizeReaction(analysisResults); + switch(rrs.getSubtype(analysisResults)) { + case CREATION: + SpeciesContext scCreated = rrs.getCreatedSpecies(speciesContextSpecs, analysisResults); + // if it doesn't exist, we should make a seed species exactly like the product pattern + // and with the same name (maybe just make an Issue) + creationMap.put(rrs, scCreated); + break; + case DECAY: + SpeciesContext scDestroyed = rrs.getDestroyedSpecies(speciesContextSpecs, analysisResults); + // see above, we need a seed species to match the reactant pattern + decayMap.put(rrs, scDestroyed); + break; + case TRANSITION: + transitionMap.put(rrs, analysisResults); + break; + case ALLOSTERIC: + allostericMap.put(rrs, analysisResults); + break; + case BINDING: + bindingMap.put(rrs, analysisResults); + break; + default: + break; + } + } + // prepare the data for the creation/decay output + for(SpeciesContext sc : model.getSpeciesContexts()) { + // skip the Source and the Sink molecules (use in Creation / Destruction reactions) + if(SpeciesContextSpec.SourceMoleculeString.equals(sc.getName()) || SpeciesContextSpec.SinkMoleculeString.equals(sc.getName())) { + continue; + } + moleculeCreationDecayRates.put(sc, new Pair("0.0", "0.0")); // initialize all species with zero + } + for (Map.Entry entry : creationMap.entrySet()) { + ReactionRuleSpec rrs = entry.getKey(); + SpeciesContext sc = entry.getValue(); + Pair oldPair = moleculeCreationDecayRates.get(sc); + if(oldPair == null) { + throw new RuntimeException("Molecule being created not found in the list of Species"); + } + RbmKineticLaw kineticLaw = rrs.getReactionRule().getKineticLaw(); + Expression creationRate = kineticLaw.getLocalParameterValue(RbmKineticLaw.RbmKineticLawParameterType.MassActionForwardRate); + Pair newPair = new Pair<> (creationRate.infix(), oldPair.two); + moleculeCreationDecayRates.put(sc, newPair); + } + for (Map.Entry entry : decayMap.entrySet()) { + ReactionRuleSpec rrs = entry.getKey(); + SpeciesContext sc = entry.getValue(); + Pair oldPair = moleculeCreationDecayRates.get(sc); + if(oldPair == null) { + throw new RuntimeException("Molecule being destroyed not found in the list of Species"); + } + RbmKineticLaw kineticLaw = rrs.getReactionRule().getKineticLaw(); + Expression decayRate = kineticLaw.getLocalParameterValue(RbmKineticLaw.RbmKineticLawParameterType.MassActionForwardRate); + Pair newPair = new Pair<> (oldPair.one, decayRate.infix()); + moleculeCreationDecayRates.put(sc, newPair); + } + } catch(Exception ex) { + throw new RuntimeException("Failed to categorize the reaction rules by SpringSaLaD subtype: " + ex.getMessage()); + } + + try { + reactionContext.convertSpeciesIniCondition(false); // convert to count if currently is concentration + + StringBuilder sb = new StringBuilder(); + /* ********* BEGIN BY WRITING THE TIMES *********/ + sb.append("*** " + TIME_INFORMATION + " ***"); + sb.append("\n"); + writeTimeInformation(sb, simulation); + sb.append("\n"); + + /* ********* WRITE THE SPATIAL INFORMATION **********/ + sb.append("*** " + SPATIAL_INFORMATION + " ***"); + sb.append("\n"); + geometryContext.writeData(sb); // TODO: need geometry + sb.append("\n"); + + /* ******* WRITE THE SPECIES INFORMATION ***********/ + sb.append("*** " + MOLECULES + " ***"); + sb.append("\n"); + sb.append("\n"); + for(SpeciesContext sc : model.getSpeciesContexts()) { + SpeciesContextSpec scs = reactionContext.getSpeciesContextSpec(sc); + scs.writeData(sb); + } + /* ******* WRITE THE MOLECULE INFORMATION FILES ***********/ + sb.append("*** " + MOLECULE_FILES + " ***"); + sb.append("\n"); + sb.append("\n"); + for(SpeciesContext sc : model.getSpeciesContexts()) { + // skip the Source and the Sink molecules (use in Creation / Destruction reactions) + if(SpeciesContextSpec.SourceMoleculeString.equals(sc.getName()) || SpeciesContextSpec.SinkMoleculeString.equals(sc.getName())) { + continue; + } + SpeciesContextSpec scs = reactionContext.getSpeciesContextSpec(sc); + sb.append("MOLECULE: " + sc.getName() + " " + scs.getFilename()); + sb.append("\n"); + } + sb.append("\n"); + + /* ******* WRITE THE DECAY REACTIONS ***************/ + sb.append("*** " + DECAY_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + ReactionRuleSpec.writeDecayData(sb, moleculeCreationDecayRates); + sb.append("\n"); + + /* ******* WRITE THE TRANSITION REACTIONS **********/ + sb.append("*** " + TRANSITION_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + for(ReactionRuleSpec rrs : reactionRuleSpecs) { + if(rrs.isExcluded()) { + continue; + } + rrs.writeData(sb, ReactionRuleSpec.Subtype.TRANSITION); + } + sb.append("\n"); + + /* ******* WRITE THE ALLOSTERIC REACTIONS **********/ + sb.append("*** " + ALLOSTERIC_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + for(ReactionRuleSpec rrs : reactionRuleSpecs) { + if(rrs.isExcluded()) { + continue; + } + rrs.writeData(sb, ReactionRuleSpec.Subtype.ALLOSTERIC); + } + sb.append("\n"); + + /* ******* WRITE THE BINDING REACTIONS ************/ + sb.append("*** " + BINDING_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + for(ReactionRuleSpec rrs : reactionRuleSpecs) { + if(rrs.isExcluded()) { + continue; + } + rrs.writeData(sb, ReactionRuleSpec.Subtype.BINDING); + } + sb.append("\n"); + + /* ****** WRITE THE MOLECULE COUNTERS **********/ + sb.append("*** " + MOLECULE_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(Molecule molecule: molecules) { +// molecule.getMoleculeCounter().writeMoleculeCounter(sb); +// } + Simulation.Counters.writeMoleculeCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* ****** WRITE THE STATE COUNTERS *************/ + sb.append("*** " + STATE_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(Molecule molecule : molecules) { +// for(SiteType type : molecule.getTypeArray()) { +// for(State state : type.getStates()) { +// state.getStateCounter().writeStateCounter(sb); +// } +// } +// } + Simulation.Counters.writeStateCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* ***** WRITE THE BOND COUNTERS ***************/ + sb.append("*** " + BOND_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(BindingReaction reaction: bindingReactions) { +// reaction.getBondCounter().writeBondCounter(sb); +// } + Simulation.Counters.writeBondCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* ******** WRITE THE SITE PROPERTY COUNTERS ************/ + sb.append("*** " + SITE_PROPERTY_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(Molecule molecule : molecules) { +// ArrayList sites = molecule.getSiteArray(); +// for(Site site : sites) { +// site.getPropertyCounter().writeSitePropertyCounter(sb); +// } +// } + Simulation.Counters.writeSitePropertyCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* *************** WRITE THE TRACK CLUSTERS BOOLEAN ***********/ + sb.append("*** " + CLUSTER_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); + sb.append("Track_Clusters: " + SpeciesContextSpec.TrackClusters); + sb.append("\n"); + sb.append("\n"); + + /* ****** WRITE THE SYSTEM ANNOTATION ********************/ + sb.append("*** " + SYSTEM_ANNOTATION + " ***"); + sb.append("\n"); + sb.append("\n"); +// systemAnnotation.printAnnotation(sb); + sb.append("\n"); + + /* ****** WRITE THE MOLECULE ANNOTATIONS *****************/ + sb.append("*** " + MOLECULE_ANNOTATIONS + " ***"); + sb.append("\n"); + sb.append("\n"); +// writeMoleculeAnnotations(sb); + sb.append("\n"); + + /* ****** WRITE THE REACTION ANNOTATIONS *****************/ + sb.append("*** " + REACTION_ANNOTATIONS + " ***"); + sb.append("\n"); + sb.append("\n"); +// writeReactionAnnotations(sb); + sb.append("\n"); + + String ret = sb.toString(); + System.out.println(ret); + return ret; + } catch(Exception ex) { + ex.printStackTrace(System.out); + } + return null; + } + + private static void writeTimeInformation(StringBuilder sb, Simulation simulation) { + // general stuff is in solver task description + simulation.getSolverTaskDescription().writeData(sb); // TODO: need proper sim + + LangevinSimulationOptions lso = simulation.getSolverTaskDescription().getLangevinSimulationOptions(); + sb.append("dt_spring: " + lso.getIntervalSpring()); // 1.00E-9 default + sb.append("\n"); + sb.append("dt_image: " + lso.getIntervalImage()); // 1.00E-4 default + sb.append("\n"); + + } + + public void writeDocumentStringToFile(String resultString, String parent, String name, Component parentComponent) throws IOException { + // make directory for the whole project if needed + // save just the biomodel string as main project file + // MainGUI.java saveItemActionPerformed() + String child = name + ".ssld"; // alttest.ssld + JPanel myPanel = new JPanel(); + + // TODO: we don't need a project folder when exporting since all project data will be in the vcell database + // we only keep this code for historic reasons while implementing the springsalad app as part of vcell + myPanel.add(new JLabel("Do you want to create a project folder?\n(Press no if saving an already existing model)")); + int n = JOptionPane.showConfirmDialog(parentComponent, myPanel, "Create Folder Option", JOptionPane.YES_NO_OPTION); + if(n == JOptionPane.YES_OPTION) { + parent = parent + File.separator + name; // C:\TEMP\ss2vcell-test\dantest\alttest + new File(parent).mkdir(); +// File f = new File(parent + File.separator + child); // C:\TEMP\ss2vcell-test\dantest\alttest\alttest.txt +// g.setFile(f); + + File targetDir = new File(parent + File.separator + "structure_files"); // C:\TEMP\ss2vcell-test\dantest\alttest\structure_files + if (!targetDir.exists() || !targetDir.isDirectory()) { + targetDir.mkdir(); + } + } + String fileName = parent + File.separator + child; + writeStringToFile(resultString, fileName, false); + System.out.println("finished exporting to ssld"); + } + + private static void writeStringToFile(String string, String filename, boolean bUseUTF8) throws IOException { + File outputFile = new File(filename); + OutputStreamWriter fileOSWriter = null; + if (bUseUTF8) { + fileOSWriter = new OutputStreamWriter(new FileOutputStream(outputFile),"UTF-8"); + } else { + fileOSWriter = new OutputStreamWriter(new FileOutputStream(outputFile)); + } + fileOSWriter.write(string); + fileOSWriter.flush(); + fileOSWriter.close(); + } + +/* + public void copyPDBtoNewLocation(){ + for (Molecule m: molecules) { + if(m.getFilename() != null) { + File source = new File(m.getFilename()); + String name = source.getName(); + File targetDir = new File(file.getParent() + File.separator + "structure_files"); + File target = new File(file.getParent() + File.separator + "structure_files" + File.separator + name); + + if (targetDir.exists() && targetDir.isDirectory() && !target.exists() && !target.isDirectory()) { + //if target does not exist, open writer + try(PrintWriter p = new PrintWriter(new FileWriter(target), true)){ + //open reader of source + try (BufferedReader br = new BufferedReader(new FileReader(source))) { + String line; + while ((line = br.readLine()) != null) { + p.println(line); + } + } + + m.setFile(target.toString()); + }catch(IOException ioe){ + ioe.printStackTrace(System.out); + } + } + } + } + } +*/ +} diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/GeometryContext.java b/vcell-core/src/main/java/cbit/vcell/mapping/GeometryContext.java index 2f8dc247d5..a30055257e 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/GeometryContext.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/GeometryContext.java @@ -37,6 +37,7 @@ import cbit.vcell.geometry.GeometrySpec; import cbit.vcell.geometry.SubVolume; import cbit.vcell.geometry.SurfaceClass; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.StructureMapping.StructureMappingParameter; import cbit.vcell.model.Feature; import cbit.vcell.model.Membrane; @@ -930,4 +931,27 @@ public void enforceHierarchicalBoundaryConditions(StructureTopology structureTop } } +public void writeData(StringBuilder sb) { // SpringSaLaD exporting the time information + if(getSimulationContext().getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + sb.append("L_x: 0.1"); + sb.append("\n"); + sb.append("L_y: 0.1"); + sb.append("\n"); + sb.append("L_z_out: 0.01"); + sb.append("\n"); + sb.append("L_z_in: 0.09"); + sb.append("\n"); + sb.append("Partition Nx: 10"); + sb.append("\n"); + sb.append("Partition Ny: 10"); + sb.append("\n"); + sb.append("Partition Nz: 10"); + sb.append("\n"); + return; +} + + } diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java b/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java new file mode 100644 index 0000000000..8b35b3151d --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java @@ -0,0 +1,1429 @@ +package cbit.vcell.mapping; + +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + + +import java.beans.PropertyVetoException; +import java.util.*; + +import org.vcell.model.rbm.ComponentStateDefinition; +import org.vcell.model.rbm.ComponentStatePattern; +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularType; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.util.Pair; +import org.vcell.util.TokenMangler; + +import cbit.vcell.geometry.GeometryClass; +import cbit.vcell.geometry.SubVolume; +import cbit.vcell.geometry.SurfaceClass; +import cbit.vcell.mapping.ParameterContext.LocalParameter; +import cbit.vcell.mapping.ParameterContext.UnresolvedParameter; +import cbit.vcell.mapping.ReactionRuleSpec.Subtype; +import cbit.vcell.mapping.ReactionRuleSpec.TransitionCondition; +import cbit.vcell.mapping.RulebasedTransformer.Operation; +import cbit.vcell.mapping.RulebasedTransformer.ReactionRuleAnalysisReport; +import cbit.vcell.mapping.RulebasedTransformer.RulebasedTransformation; +import cbit.vcell.mapping.SimulationContext.MathMappingCallback; +import cbit.vcell.mapping.SimulationContext.NetworkGenerationRequirements; +import cbit.vcell.math.Action; +import cbit.vcell.math.CompartmentSubDomain; +import cbit.vcell.math.Constant; +import cbit.vcell.math.JumpProcessRateDefinition; +import cbit.vcell.math.LangevinParticleJumpProcess; +import cbit.vcell.math.LangevinParticleMolecularComponent; +import cbit.vcell.math.LangevinParticleMolecularType; +import cbit.vcell.math.MacroscopicRateConstant; +import cbit.vcell.math.MathDescription; +import cbit.vcell.math.MathException; +import cbit.vcell.math.MathUtilities; +import cbit.vcell.math.MembraneSubDomain; +import cbit.vcell.math.ParticleComponentStateDefinition; +import cbit.vcell.math.ParticleComponentStatePattern; +import cbit.vcell.math.ParticleJumpProcess; +import cbit.vcell.math.ParticleJumpProcess.ProcessSymmetryFactor; +import cbit.vcell.math.ParticleMolecularComponent; +import cbit.vcell.math.ParticleMolecularComponentPattern; +import cbit.vcell.math.ParticleMolecularComponentPattern.ParticleBondType; +import cbit.vcell.math.ParticleMolecularType; +import cbit.vcell.math.ParticleMolecularTypePattern; +import cbit.vcell.math.ParticleObservable; +import cbit.vcell.math.ParticleObservable.Sequence; +import cbit.vcell.math.ParticleProperties; +import cbit.vcell.math.ParticleProperties.ParticleInitialCondition; +import cbit.vcell.math.ParticleProperties.ParticleInitialConditionCount; +import cbit.vcell.math.ParticleVariable; +import cbit.vcell.math.StochVolVariable; +import cbit.vcell.math.SubDomain; +import cbit.vcell.math.Variable; +import cbit.vcell.math.Variable.Domain; +import cbit.vcell.math.VariableHash; +import cbit.vcell.math.VolumeParticleObservable; +import cbit.vcell.math.VolumeParticleSpeciesPattern; +import cbit.vcell.math.VolumeParticleVariable; +import cbit.vcell.matrix.MatrixException; +import cbit.vcell.model.Model; +import cbit.vcell.model.Model.ModelParameter; +import cbit.vcell.model.ModelException; +import cbit.vcell.model.ModelUnitSystem; +import cbit.vcell.model.Parameter; +import cbit.vcell.model.ProductPattern; +import cbit.vcell.model.RbmKineticLaw; +import cbit.vcell.model.RbmKineticLaw.RbmKineticLawParameterType; +import cbit.vcell.model.RbmObservable; +import cbit.vcell.model.ReactantPattern; +import cbit.vcell.model.ReactionRule; +import cbit.vcell.model.SpeciesContext; +import cbit.vcell.model.Structure; +import cbit.vcell.model.Structure.StructureSize; +import cbit.vcell.parser.Expression; +import cbit.vcell.parser.ExpressionBindingException; +import cbit.vcell.parser.ExpressionException; +import cbit.vcell.parser.RationalExpUtils; +import cbit.vcell.units.VCUnitDefinition; +import jscl.text.ParseException; +/** + * The MathMapping class performs the Biological to Mathematical transformation once upon calling getMathDescription(). + * This is not a "live" transformation, so that an updated SimulationContext must be given to a new MathMapping object + * to get an updated MathDescription. + */ +public class LangevinMathMapping extends AbstractStochMathMapping { + +/** + * @param model cbit.vcell.model.Model + * @param geometry cbit.vcell.geometry.Geometry + */ +protected LangevinMathMapping(SimulationContext simContext, MathMappingCallback callback, NetworkGenerationRequirements networkGenerationRequirements) { + super(simContext, callback, networkGenerationRequirements); +} + + @Override + protected void refreshMathDescription() throws MappingException, MatrixException, MathException, ExpressionException, ModelException + { + //use local variable instead of using getter all the time. + SimulationContext simContext = getSimulationContext(); + GeometryClass geometryClass = simContext.getGeometry().getGeometrySpec().getSubVolumes()[0]; + Domain domain = new Domain(geometryClass); + + //local structure mapping list + StructureMapping structureMappings[] = simContext.getGeometryContext().getStructureMappings(); + //We have to check if all the reactions are able to transform to stochastic jump processes before generating the math. + String stochChkMsg =simContext.getModel().isValidForStochApp(); + if(!(stochChkMsg.equals(""))) + { + throw new ModelException("Problem updating math description: "+ simContext.getName()+"\n"+stochChkMsg); + } + + simContext.checkValidity(); + + // + // verify 3D + // + if (simContext.getGeometry().getDimension() != 3) { + throw new MappingException("Langevin based particle math mapping implemented for 3D spatial geometry - dimension != 3"); + } + + // + // check that we aren't solving for electric potential. + // + for (int i = 0; i < structureMappings.length; i++){ + if (structureMappings[i] instanceof MembraneMapping){ + if (((MembraneMapping)structureMappings[i]).getCalculateVoltage()){ + throw new MappingException("electric potential not yet supported for particle models"); + } + } + } + + // + // fail if any events + // + BioEvent[] bioEvents = simContext.getBioEvents(); + if (bioEvents!=null && bioEvents.length>0){ + throw new MappingException("events not yet supported for particle-based models"); + } + + Structure structures[] = simContext.getGeometryContext().getModel().getStructures(); + // + // verify 3 structures: 2 Features and 1 Membrane + // + int numFeatures = 0; + int numMembranes = 0; + for(Structure structure : structures) { + int dimension = structure.getDimension(); + if(dimension == 2) { + numMembranes++; + } else if(dimension == 3) { + numFeatures++; + } + } + if(numFeatures != 2 || numMembranes != 1) { + throw new MappingException("The model must have exactly two Features and one Membrane"); + } + + // + // verify that all structures are mapped to subvolumes and all subvolumes are mapped to a structure + // + for (int i = 0; i < structures.length; i++){ + StructureMapping sm = simContext.getGeometryContext().getStructureMapping(structures[i]); + if (sm==null || (sm instanceof FeatureMapping && ((FeatureMapping)sm).getGeometryClass() == null)){ + throw new MappingException("model structure '"+structures[i].getName()+"' not mapped to a geometry subVolume"); + } + if (sm!=null && (sm instanceof MembraneMapping) && ((MembraneMapping)sm).getVolumeFractionParameter()!=null){ + Expression volFractExp = ((MembraneMapping)sm).getVolumeFractionParameter().getExpression(); + try { + if(volFractExp != null) + { + double volFract = volFractExp.evaluateConstant(); + if (volFract>=1.0){ + throw new MappingException("model structure '"+(getSimulationContext().getModel().getStructureTopology().getInsideFeature(((MembraneMapping)sm).getMembrane()).getName()+"' has volume fraction >= 1.0")); + } + } + }catch (ExpressionException e){ + e.printStackTrace(System.out); + } + } + } + SubVolume subVolumes[] = simContext.getGeometryContext().getGeometry().getGeometrySpec().getSubVolumes(); + for (int i = 0; i < subVolumes.length; i++){ + Structure[] mappedStructures = simContext.getGeometryContext().getStructuresFromGeometryClass(subVolumes[i]); + if (mappedStructures==null || mappedStructures.length==0){ + throw new MappingException("geometry subVolume '"+subVolumes[i].getName()+"' not mapped from a model structure"); + } + } + + // + // gather only those reactionRules that are not "excluded" + // + ArrayList rrList = new ArrayList(); + for (ReactionRuleSpec reactionRuleSpec : simContext.getReactionContext().getReactionRuleSpecs()){ + if (!reactionRuleSpec.isExcluded()){ + rrList.add(reactionRuleSpec.getReactionRule()); + } + } + + // + // fail if any unresolved parameters + // + for (ReactionRule reactionRule : rrList){ + UnresolvedParameter unresolvedParameters[] = reactionRule.getKineticLaw().getUnresolvedParameters(); + if (unresolvedParameters!=null && unresolvedParameters.length>0){ + StringBuffer buffer = new StringBuffer(); + for (int j = 0; j < unresolvedParameters.length; j++){ + if (j>0){ + buffer.append(", "); + } + buffer.append(unresolvedParameters[j].getName()); + } + throw new MappingException("In Application '" + simContext.getName() + "', " + reactionRule.getDisplayType()+" '"+reactionRule.getName()+"' contains unresolved identifier(s): "+buffer); + } + } + + // + // create new MathDescription (based on simContext's previous MathDescription if possible) + // + MathDescription oldMathDesc = simContext.getMathDescription(); + mathDesc = null; + if (oldMathDesc != null){ + if (oldMathDesc.getVersion() != null){ + mathDesc = new MathDescription(oldMathDesc.getVersion(), mathSymbolMapping); + }else{ + mathDesc = new MathDescription(oldMathDesc.getName(), mathSymbolMapping); + } + }else{ + mathDesc = new MathDescription(simContext.getName()+"_generated", mathSymbolMapping); + } + + // + // temporarily place all variables in a hashtable (before binding) and discarding duplicates + // + VariableHash varHash = new VariableHash(); + + // + // conversion factors + // + Model model = simContext.getModel(); + varHash.addVariable(new Constant(getMathSymbol(model.getKMOLE(), null), getIdentifierSubstitutions(model.getKMOLE().getExpression(),model.getKMOLE().getUnitDefinition(),null))); + varHash.addVariable(new Constant(getMathSymbol(model.getN_PMOLE(), null), getIdentifierSubstitutions(model.getN_PMOLE().getExpression(),model.getN_PMOLE().getUnitDefinition(),null))); + varHash.addVariable(new Constant(getMathSymbol(model.getPI_CONSTANT(),null), getIdentifierSubstitutions(model.getPI_CONSTANT().getExpression(),model.getPI_CONSTANT().getUnitDefinition(),null))); + varHash.addVariable(new Constant(getMathSymbol(model.getFARADAY_CONSTANT(),null), getIdentifierSubstitutions(model.getFARADAY_CONSTANT().getExpression(),model.getFARADAY_CONSTANT().getUnitDefinition(),null))); + varHash.addVariable(new Constant(getMathSymbol(model.getFARADAY_CONSTANT_NMOLE(),null), getIdentifierSubstitutions(model.getFARADAY_CONSTANT_NMOLE().getExpression(),model.getFARADAY_CONSTANT_NMOLE().getUnitDefinition(),null))); + varHash.addVariable(new Constant(getMathSymbol(model.getGAS_CONSTANT(),null), getIdentifierSubstitutions(model.getGAS_CONSTANT().getExpression(),model.getGAS_CONSTANT().getUnitDefinition(),null))); + varHash.addVariable(new Constant(getMathSymbol(model.getTEMPERATURE(),null), getIdentifierSubstitutions(new Expression(simContext.getTemperatureKelvin()), model.getTEMPERATURE().getUnitDefinition(),null))); + + Enumeration enum1 = getSpeciesContextMappings(); + while (enum1.hasMoreElements()){ + SpeciesContextMapping scm = enum1.nextElement(); + if (scm.getVariable() instanceof StochVolVariable){ + varHash.addVariable(scm.getVariable()); + } + } + + // deals with model parameters + ModelParameter[] modelParameters = simContext.getModel().getModelParameters(); + for (int j=0;j 0) { + throw new MappingException("Modles with electrophysiology are not supported for stochastic applications."); + } + + // + // add constant mem voltage + // + + for (int j = 0; j < structureMappings.length; j++){ + if (structureMappings[j] instanceof MembraneMapping){ + MembraneMapping memMapping = (MembraneMapping)structureMappings[j]; + Parameter initialVoltageParm = memMapping.getInitialVoltageParameter(); + try{ + Expression exp = initialVoltageParm.getExpression(); + exp.evaluateConstant(); + varHash.addVariable(newFunctionOrConstant(getMathSymbol(memMapping.getMembrane().getMembraneVoltage(),memMapping.getGeometryClass()), + getIdentifierSubstitutions(memMapping.getInitialVoltageParameter().getExpression(),memMapping.getInitialVoltageParameter().getUnitDefinition(),memMapping.getGeometryClass()),memMapping.getGeometryClass())); + }catch(ExpressionException e){ + e.printStackTrace(System.out); + throw new MappingException("Membrane initial voltage: "+initialVoltageParm.getName()+" cannot be evaluated as constant."); + } + } + } + + // + // kinetic parameters (functions or constants) + // + for (ReactionRule reactionRule : rrList){ +// if (reactionRule.getKineticLaw() instanceof LumpedKinetics){ +// throw new RuntimeException("Lumped Kinetics not yet supported for RuleBased Modeling"); +// } + LocalParameter parameters[] = reactionRule.getKineticLaw().getLocalParameters(); + for (LocalParameter parameter : parameters){ + // + // skip current density if not used. + // +// if ((parameter.getRole() == RbmKineticLawParameterType.ROLE_CurrentDensity) && +// (parameter.getExpression()==null || parameter.getExpression().isZero())){ +// continue; +// } + // + // don't add rate, we'll do it later when creating the jump processes + // + if ((parameter.getRole() == RbmKineticLawParameterType.RuleRate)){ + continue; + } + + // + // don't add mass action reverse parameter if irreversible + // + if (!reactionRule.isReversible() && parameter.getRole() == RbmKineticLawParameterType.MassActionReverseRate){ + continue; + } + + Expression expr = getSubstitutedExpr(parameter.getExpression(), true, false); + varHash.addVariable(newFunctionOrConstant(getMathSymbol(parameter,geometryClass), getIdentifierSubstitutions(expr,parameter.getUnitDefinition(),geometryClass),geometryClass)); + } + } + + //geometic mapping + //the parameter "Size" is already put into mathsymbolmapping in refreshSpeciesContextMapping() + for (int i=0;i molecularTypeToSpeciesContextSpecMap = new LinkedHashMap<> (); + SpeciesContextSpec speciesContextSpecs[] = getSimulationContext().getReactionContext().getSpeciesContextSpecs(); + for(SpeciesContextSpec scs : speciesContextSpecs) { + SpeciesContext sc = scs.getSpeciesContext(); + if(!sc.hasSpeciesPattern()) { + throw new RuntimeException("LangevinMathMapping: all Species must have valid SpeciesPattern"); + } + SpeciesPattern sp = sc.getSpeciesPattern(); + List molecularTypePatterns = sp.getMolecularTypePatterns(); + if(molecularTypePatterns== null || molecularTypePatterns.size() != 1) { + throw new RuntimeException("LangevinMathMapping: all Species must have exactly one MolecularType"); + } + MolecularType mt = molecularTypePatterns.get(0).getMolecularType(); + if(molecularTypeToSpeciesContextSpecMap.containsKey(mt)) { + throw new RuntimeException("LangevinMathMapping: only one Species can be defined for each MolecularType"); + } + molecularTypeToSpeciesContextSpecMap.put(mt, scs); + } + addInitialConditions(domain, speciesContextSpecs, varHash); + + // + // geometry + // + if (simContext.getGeometryContext().getGeometry() != null){ + try { + mathDesc.setGeometry(simContext.getGeometryContext().getGeometry()); + }catch (java.beans.PropertyVetoException e){ + e.printStackTrace(System.out); + throw new MappingException("failure setting geometry "+e.getMessage()); + } + }else{ + throw new MappingException("Geometry must be defined in Application "+simContext.getName()); + } + + // + // create subDomains (volume and surfaces) + // + // TODO: make a function in ParticleMapMapping with identical code and call it here + // + GeometryClass[] geometryClasses = getSimulationContext().getGeometryContext().getGeometry().getGeometryClasses(); + for (int k=0;k1){ + subDomain.setBoundaryConditionYm(mappedFM.getBoundaryConditionTypeYm()); + subDomain.setBoundaryConditionYp(mappedFM.getBoundaryConditionTypeYp()); + } + if (getSimulationContext().getGeometry().getDimension()>2){ + subDomain.setBoundaryConditionZm(mappedFM.getBoundaryConditionTypeZm()); + subDomain.setBoundaryConditionZp(mappedFM.getBoundaryConditionTypeZp()); + } + } + }else if (geometryClasses[k] instanceof SurfaceClass){ + SurfaceClass surfaceClass = (SurfaceClass)geometryClasses[k]; + // determine membrane inside and outside subvolume + // this preserves backward compatibility so that membrane subdomain + // inside and outside correspond to structure hierarchy when present + Pair ret = DiffEquMathMapping.computeBoundaryConditionSource(model, simContext, surfaceClass); + SubVolume innerSubVolume = ret.one; + SubVolume outerSubVolume = ret.two; + + // + // create subDomain + // + CompartmentSubDomain outerCompartment = mathDesc.getCompartmentSubDomain(outerSubVolume.getName()); + CompartmentSubDomain innerCompartment = mathDesc.getCompartmentSubDomain(innerSubVolume.getName()); + + MembraneSubDomain memSubDomain = new MembraneSubDomain(innerCompartment,outerCompartment,surfaceClass.getName()); + mathDesc.addSubDomain(memSubDomain); + } + } + + // + // define all molecules and unique species patterns (add molecules to mathDesc and speciesPatterns to varHash). + // + HashMap speciesPatternMap = addSpeciesPatterns(domain, rrList, molecularTypeToSpeciesContextSpecMap); + HashSet uniqueParticleSpeciesPatterns = new HashSet<>(speciesPatternMap.values()); + for (VolumeParticleSpeciesPattern volumeParticleSpeciesPattern : uniqueParticleSpeciesPatterns){ + varHash.addVariable(volumeParticleSpeciesPattern); + } + + // + // define observables (those explicitly declared and those corresponding to seed species. + // + // TODO: we don't have observables per se, we have statistics that we follow + // keep them for now, will have to replace them at some point + // + List observables = addObservables(geometryClass, domain, speciesPatternMap); + for (ParticleObservable particleObservable : observables){ + varHash.addVariable(particleObservable); + } + + // define reaction rules + try { + addParticleJumpProcesses(varHash, geometryClass, speciesPatternMap); + } catch (PropertyVetoException e1) { + e1.printStackTrace(); + throw new MappingException(e1.getMessage(),e1); + } + + // + // include required UnitRateFactors + // + for (int i = 0; i < fieldMathMappingParameters.length; i++){ + if (fieldMathMappingParameters[i] instanceof UnitFactorParameter || fieldMathMappingParameters[i] instanceof ObservableConcentrationParameter){ + varHash.addVariable(newFunctionOrConstant(getMathSymbol(fieldMathMappingParameters[i],geometryClass),getIdentifierSubstitutions(fieldMathMappingParameters[i].getExpression(),fieldMathMappingParameters[i].getUnitDefinition(),geometryClass),fieldMathMappingParameters[i].getGeometryClass())); + } + } + + // + // set up particle initial conditions in subdomain + // + for (SpeciesContext sc : model.getSpeciesContexts()){ + if(!sc.hasSpeciesPattern()) { + throw new MappingException("species "+sc.getName()+" has no molecular pattern"); + } + VolumeParticleSpeciesPattern volumeParticleSpeciesPattern = speciesPatternMap.get(sc.getSpeciesPattern()); + ArrayList particleInitialConditions = new ArrayList(); + + SpeciesContextSpec scs = simContext.getReactionContext().getSpeciesContextSpec(sc); // initial conditions from scs + Parameter initialCountParameter = scs.getInitialCountParameter(); + Expression e = getIdentifierSubstitutions(new Expression(initialCountParameter,getNameScope()),initialCountParameter.getUnitDefinition(),geometryClass); + particleInitialConditions.add(new ParticleInitialConditionCount(e,new Expression(0.0),new Expression(0.0),new Expression(0.0))); + + ParticleProperties particleProperies = new ParticleProperties(volumeParticleSpeciesPattern,new Expression(0.0),new Expression(0.0),new Expression(0.0),new Expression(0.0),particleInitialConditions); + StructureMapping sm = getSimulationContext().getGeometryContext().getStructureMapping(sc.getStructure()); + GeometryClass reactionStepGeometryClass = sm.getGeometryClass(); + SubDomain subDomain = mathDesc.getSubDomain(reactionStepGeometryClass.getName()); + subDomain.addParticleProperties(particleProperies); + } + + // + // add any missing unit conversion factors (they don't depend on anyone else ... can do it at the end) + // + for (int i = 0; i < fieldMathMappingParameters.length; i++){ + if (fieldMathMappingParameters[i] instanceof UnitFactorParameter){ + Variable variable = newFunctionOrConstant(getMathSymbol(fieldMathMappingParameters[i],geometryClass),getIdentifierSubstitutions(fieldMathMappingParameters[i].getExpression(),fieldMathMappingParameters[i].getUnitDefinition(),geometryClass),fieldMathMappingParameters[i].getGeometryClass()); + if (varHash.getVariable(variable.getName())==null){ + varHash.addVariable(variable); + } + } + if (fieldMathMappingParameters[i] instanceof ObservableConcentrationParameter){ + Variable variable = newFunctionOrConstant(getMathSymbol(fieldMathMappingParameters[i],geometryClass),getIdentifierSubstitutions(fieldMathMappingParameters[i].getExpression(),fieldMathMappingParameters[i].getUnitDefinition(),geometryClass),fieldMathMappingParameters[i].getGeometryClass()); + if (varHash.getVariable(variable.getName())==null){ + varHash.addVariable(variable); + } + } + } + + // + // set Variables to MathDescription all at once with the order resolved by "VariableHash" + // + mathDesc.setAllVariables(varHash.getAlphabeticallyOrderedVariables()); + + mathDesc.refreshDependencies(); + + if (!mathDesc.isValid()){ + if (lg.isTraceEnabled()) { + lg.trace(mathDesc.getVCML_database()); + } + throw new MappingException("generated an invalid mathDescription: "+mathDesc.getWarning()); + } + } + + private void addParticleJumpProcesses(VariableHash varHash, GeometryClass geometryClass, HashMap speciesPatternMap) throws ExpressionException, MappingException, MathException, PropertyVetoException { + + ArrayList rrList = new ArrayList<>(); + for (ReactionRuleSpec rrSpec : getSimulationContext().getReactionContext().getReactionRuleSpecs()){ + if (!rrSpec.isExcluded()){ + rrList.add(rrSpec.getReactionRule()); + } + } + + for (ReactionRule reactionRule : rrList) { + // recover the SpringSaLaD specific reaction parameters + ReactionRuleSpec reactionRuleSpec = getSimulationContext().getReactionContext().getReactionRuleSpec(reactionRule); + Map analysisResults = new LinkedHashMap<> (); + reactionRuleSpec.analizeReaction(analysisResults); + ReactionRuleSpec.Subtype subtype = reactionRuleSpec.getSubtype(analysisResults); + if(Subtype.INCOMPATIBLE == subtype) { + continue; // skip unfit reactions, we want to create a compatible math + } + ReactionRuleSpec.TransitionCondition transitionCondition = reactionRuleSpec.getTransitionCondition(analysisResults); + double bondLength = reactionRuleSpec.getFieldBondLength(); + + String jpName = TokenMangler.mangleToSName(reactionRule.getName()); + + ArrayList reactantParticles = new ArrayList(); + for (ReactantPattern reactantSpeciesPattern : reactionRule.getReactantPatterns()){ + reactantParticles.add(speciesPatternMap.get(reactantSpeciesPattern.getSpeciesPattern())); + } + ArrayList productParticles = new ArrayList(); + for (ProductPattern productSpeciesPattern : reactionRule.getProductPatterns()) { + productParticles.add(speciesPatternMap.get(productSpeciesPattern.getSpeciesPattern())); + } + ArrayList forwardActions = new ArrayList(); + ArrayList reverseActions = new ArrayList(); + for (ParticleVariable reactant : reactantParticles) { + forwardActions.add(new Action(reactant,Action.ACTION_DESTROY,new Expression(1.0))); + reverseActions.add(new Action(reactant,Action.ACTION_CREATE,new Expression(1.0))); + } + for (ParticleVariable product : productParticles) { + forwardActions.add(new Action(product,Action.ACTION_CREATE,new Expression(1.0))); + reverseActions.add(new Action(product,Action.ACTION_DESTROY,new Expression(1.0))); + } + RbmKineticLaw kinetics = reactionRule.getKineticLaw(); + + // check the reaction rate law to see if we need to decompose a reaction(reversible) into two jump processes. + // rate constants are important in calculating the probability rate. + // for Mass Action, we use KForward and KReverse, + // for General Kinetics we parse reaction rate J to see if it is in Mass Action form. + if (kinetics.getRateLawType() == RbmKineticLaw.RateLawType.MassAction) { + boolean constantMassActionKineticCoefficients = true; + + StringBuffer errorMessage = new StringBuffer(); + Parameter forward_rateParameter = kinetics.getLocalParameter(RbmKineticLawParameterType.MassActionForwardRate); + Expression substitutedForwardRate = MathUtilities.substituteModelParameters(forward_rateParameter.getExpression(), reactionRule.getNameScope().getScopedSymbolTable()); + if (!substitutedForwardRate.flatten().isNumeric()) { + errorMessage.append("flattened Kf for reactionRule("+reactionRule.getName()+") is not numeric, exp = '"+substitutedForwardRate.flatten().infix()+"'"); + constantMassActionKineticCoefficients = false; + } + if (reactionRule.isReversible()) { + Parameter reverse_rateParameter = kinetics.getLocalParameter(RbmKineticLawParameterType.MassActionReverseRate); + if (reverse_rateParameter==null || reverse_rateParameter.getExpression()==null){ + throw new MappingException("reverse rate constant for reaction rule "+reactionRule.getName()+" is missing"); + } + Expression substitutedReverseRate = MathUtilities.substituteModelParameters(reverse_rateParameter.getExpression(), reactionRule.getNameScope().getScopedSymbolTable()); + if (!substitutedReverseRate.flatten().isNumeric()) { + errorMessage.append("flattened Kr for reactionRule("+reactionRule.getName()+") is not numeric, exp = '"+substitutedReverseRate.flatten().infix()+"'"); + constantMassActionKineticCoefficients = false; + } + } + + StructureMapping sm = getSimulationContext().getGeometryContext().getStructureMapping(reactionRule.getStructure()); + GeometryClass reactionStepGeometryClass = sm.getGeometryClass(); + SubDomain subDomain = mathDesc.getSubDomain(reactionStepGeometryClass.getName()); + if (constantMassActionKineticCoefficients) { + addStrictMassActionParticleJumpProcess(varHash, geometryClass, subDomain, + reactionRule, jpName, + subtype, transitionCondition, bondLength, // SpringSaLaD specific + reactantParticles, productParticles, + forwardActions, reverseActions); + } else { + throw new MappingException("not mass action: "+errorMessage.toString()); +// addGeneralParticleJumpProcess(varHash, geometryClass, subDomain, +// reactionRule, jpName, +// reactantParticles, productParticles, +// forwardActions, reverseActions); + } + } else { + throw new MappingException("rule-based math generation unsupported for Kinetic Law: "+kinetics.getRateLawType()); + } + + } // end reactionRules + } + +// private void addGeneralParticleJumpProcess_NOT_USED(VariableHash varHash, GeometryClass geometryClass, SubDomain subDomain, +// ReactionRule reactionRule, String jpName, +// ArrayList reactantParticles, ArrayList productParticles, +// ArrayList forwardActions, ArrayList reverseActions) +// throws ExpressionException, ExpressionBindingException, PropertyVetoException, MathException, MappingException { +// +// // +// // don't forget to add rule analysis operations here. +// // +// String reactionRuleName = reactionRule.getName(); +// RbmKineticLaw kinetics = reactionRule.getKineticLaw(); +// +// if (kinetics.getRateLawType() != RbmKineticLaw.RateLawType.MassAction){ +// throw new RuntimeException("expecting mass action kinetics for reaction rule "+reactionRuleName); +// } +// +// // +// // construct stochastic forward or reverse rate expression (separately). Transform from +// // original expression of "concentrationRate" in terms of rateParameter and reactants/products in concentrations +// // to +// // new stochastic expression of "molecularRate" in terms of forwardRateParameter, reactants/products in molecules, structure sizes, and unit conversions. +// // +// // (1) concentrationRate = K * [s0] * [s1] [uM.s-1] or [molecules.um-3.s-1] or [molecules.um-2.s-1] (or other) +// // (2) molecularRate = P * * [molecules.s-1] +// // +// // in this math description, we are using [molecules], but original kinetics were in [s_i] [uM or molecules.um-2]. +// // so through a change in variable to get things in terms of . <<<< Here P is the desired stochastic rate coefficient. >>> +// // +// // (3) let [s_i] = /structsize(s_i)*unitConversionFactor(substanceunit([s_i])/substanceunit()) +// // +// // in addition to the change in variables, we need to transform the entire expression from concentration/time to molecules/time +// // +// // (4) let molecularRate = concentrationRate * structSize(reaction) * unitConversionFactor(substanceunit(molecularRate)/substanceunit(concentrationRate)) +// // +// // (5) in general, concentationRate = K * PRODUCT([s_i]) +// // +// // change of variables into stochastic variables used in MathDescription, substituting (3) into (5) +// // +// // (6) concentrationRate = K * PRODUCT(/structsize(s_i)*unitConversionFactor(substanceunit([s_i])/substanceunit())) +// // +// // reordering to separate the sizes, the unit conversions and the +// // +// // (7) concentrationRate = K * PRODUCT() * PRODUCT(1/structsize(s_i)) * unitConversionFactor(PRODUCT(substanceunit([s_i])/substanceunit())) +// // +// // combining (4) and (7) +// // +// // (8) molecularRate = K * PRODUCT() * PRODUCT(1/structsize(s_i)) * unitConversionFactor(PRODUCT(substanceunit([s_i])/substanceunit())) * structSize(reaction) * unitConversionFactor(substanceunit(molecularRate)/substanceunit(concentrationRate)) +// // +// // collecting terms of sizes and unit conversions +// // +// // (9) molecularRate = K * PRODUCT() * structSize(reaction) / PRODUCT(structsize(s_i)) * unitConversionFactor(substanceunit(molecularRate)/substanceunit(concentrationRate) * PRODUCT(substanceunit([s_i])/substanceunit())) +// // +// // (10) molecularRate = K * PRODUCT() * sizeFactor * unitConversionFactor(substanceConversionUnit) +// // +// // where +// // +// // (11) sizeFactor = structSize(reaction) / PRODUCT(structsize(s_i)) +// // (12) substanceConversionUnit = substanceunit(molecularRate)/substanceunit(concentrationRate) * PRODUCT(substanceunit([s_i])/substanceunit()) +// // +// // The ParticleJumpCondition wants a single new rate stochastic, P from equation (2). Note that PRODUCT() will be captured separately the the reactantPatterns. +// // comparing (2) and (10) we have found P. +// // +// // (13) P = K * sizeFactor * unitConversionFactor(substanceConversionUnit) +// // +// // the framework also needs the proper unit for P +// // +// // (14) Unit(P) = Unit(K) * Unit(sizeFactor) * substanceConversionUnit +// // +// // +// +// ModelUnitSystem modelUnitSystem = getSimulationContext().getModel().getUnitSystem(); +// VCUnitDefinition stochasticSubstanceUnit = modelUnitSystem.getStochasticSubstanceUnit(); +// VCUnitDefinition reactionRuleSubstanceUnit = modelUnitSystem.getSubstanceUnit(reactionRule.getStructure()); +// +// { +// // +// // get forward rate parameter and make sure it is constant valued. +// // +// Parameter forward_rateParameter = kinetics.getLocalParameter(RbmKineticLawParameterType.MassActionForwardRate); +// { +// Expression substitutedForwardRate = MathUtilities.substituteModelParameters(forward_rateParameter.getExpression(), reactionRule.getNameScope().getScopedSymbolTable()); +// if (!substitutedForwardRate.flatten().isNumeric()){ +//// throw new MappingException("forward rate constant for reaction rule "+reactionRule.getName()+" is not constant"); +// } +// } +// +// // +// // create forward sizeExp and forward unitFactor +// // +// VCUnitDefinition forward_substanceConversionUnit = stochasticSubstanceUnit.divideBy(reactionRuleSubstanceUnit); +// VCUnitDefinition forward_sizeFactorUnit = reactionRule.getStructure().getStructureSize().getUnitDefinition(); +// Expression forward_sizeFactor = new Expression(reactionRule.getStructure().getStructureSize(),getNameScope()); +// for (ReactantPattern reactantPattern : reactionRule.getReactantPatterns()){ +// Expression reactantSizeExp = new Expression(reactantPattern.getStructure().getStructureSize(),getNameScope()); +// VCUnitDefinition reactantSizeUnit = reactantPattern.getStructure().getStructureSize().getUnitDefinition(); +// VCUnitDefinition reactantSubstanceUnit = modelUnitSystem.getSubstanceUnit(reactantPattern.getStructure()); +// forward_sizeFactor = Expression.div(forward_sizeFactor,reactantSizeExp); +// forward_sizeFactorUnit = forward_sizeFactorUnit.divideBy(reactantSizeUnit); +// forward_substanceConversionUnit = forward_substanceConversionUnit.multiplyBy(reactantSubstanceUnit).divideBy(stochasticSubstanceUnit); +// } +// // simplify sizeFactor (often has size/size/size) +// try { +// forward_sizeFactor = RationalExpUtils.getRationalExp(forward_sizeFactor).simplifyAsExpression(); +// forward_sizeFactor.bindExpression(getSimulationContext().getModel()); +// } catch (ParseException e) { +// e.printStackTrace(); +// } +// +// Expression forward_rateExp = Expression.mult(new Expression(forward_rateParameter, getNameScope()),forward_sizeFactor,getUnitFactor(forward_substanceConversionUnit)).flatten(); +// VCUnitDefinition forward_rateUnit = forward_rateParameter.getUnitDefinition().multiplyBy(forward_sizeFactorUnit).multiplyBy(forward_substanceConversionUnit); +// +// ProbabilityParameter forward_probParm = addProbabilityParameter(PARAMETER_PROBABILITYRATE_PREFIX+jpName, forward_rateExp, PARAMETER_ROLE_P, forward_rateUnit,reactionRule); +// +// //add probability to function or constant +// varHash.addVariable(newFunctionOrConstant(getMathSymbol(forward_probParm,geometryClass),getIdentifierSubstitutions(forward_rateExp, forward_rateUnit, geometryClass),geometryClass)); +// +// // add forward ParticleJumpProcess +// String forward_name = reactionRuleName; +// Expression forward_rate = getIdentifierSubstitutions(new Expression(forward_probParm,getNameScope()), forward_probParm.getUnitDefinition(), geometryClass); +// JumpProcessRateDefinition forward_rateDefinition = new MacroscopicRateConstant(forward_rate); +// ParticleJumpProcess forward_particleJumpProcess = new ParticleJumpProcess(forward_name,reactantParticles,forward_rateDefinition,forwardActions); +// subDomain.addParticleJumpProcess(forward_particleJumpProcess); +// } +// +// // +// // get reverse rate parameter and make sure it is missing or constant valued. +// // +// if (reactionRule.isReversible()){ +// Parameter reverse_rateParameter = kinetics.getLocalParameter(RbmKineticLawParameterType.MassActionReverseRate); +// if (reverse_rateParameter==null || reverse_rateParameter.getExpression()==null){ +// throw new MappingException("reverse rate constant for reaction rule "+reactionRule.getName()+" is missing"); +// } +// { +// Expression substitutedReverseRate = MathUtilities.substituteModelParameters(reverse_rateParameter.getExpression(), reactionRule.getNameScope().getScopedSymbolTable()); +// if (!substitutedReverseRate.flatten().isNumeric()){ +// throw new MappingException("reverse rate constant for reaction rule "+reactionRule.getName()+" is not constant"); +// } +// } +// +// // +// // create reverse sizeExp and reverse unitFactor +// // +// VCUnitDefinition reverse_substanceConversionUnit = stochasticSubstanceUnit.divideBy(reactionRuleSubstanceUnit); +// VCUnitDefinition reverse_sizeFactorUnit = reactionRule.getStructure().getStructureSize().getUnitDefinition(); +// Expression reverse_sizeFactor = new Expression(reactionRule.getStructure().getStructureSize(),getNameScope()); +// for (ProductPattern productPattern : reactionRule.getProductPatterns()){ +// Expression reactantSizeExp = new Expression(productPattern.getStructure().getStructureSize(),getNameScope()); +// VCUnitDefinition reactantSizeUnit = productPattern.getStructure().getStructureSize().getUnitDefinition(); +// VCUnitDefinition reactantSubstanceUnit = modelUnitSystem.getSubstanceUnit(productPattern.getStructure()); +// reverse_sizeFactor = Expression.div(reverse_sizeFactor,reactantSizeExp); +// reverse_sizeFactorUnit = reverse_sizeFactorUnit.divideBy(reactantSizeUnit); +// reverse_substanceConversionUnit = reverse_substanceConversionUnit.multiplyBy(reactantSubstanceUnit).divideBy(stochasticSubstanceUnit); +// } +// // simplify sizeFactor (often has size/size/size) +// try { +// reverse_sizeFactor = RationalExpUtils.getRationalExp(reverse_sizeFactor).simplifyAsExpression(); +// reverse_sizeFactor.bindExpression(getSimulationContext().getModel()); +// } catch (ParseException e) { +// e.printStackTrace(); +// } +// +// Expression reverse_rateExp = Expression.mult(new Expression(reverse_rateParameter, getNameScope()),reverse_sizeFactor,getUnitFactor(reverse_substanceConversionUnit)).flatten(); +// VCUnitDefinition reverse_rateUnit = reverse_rateParameter.getUnitDefinition().multiplyBy(reverse_sizeFactorUnit).multiplyBy(reverse_substanceConversionUnit); +// +// // if the reaction has forward rate (Mass action,HMMs), or don't have either forward or reverse rate (some other rate laws--like general) +// // we process it as forward reaction +// // get jump process name +// +// ProbabilityParameter reverse_probParm = addProbabilityParameter(PARAMETER_PROBABILITYRATE_PREFIX+jpName+"_reverse", reverse_rateExp, PARAMETER_ROLE_P_reverse, reverse_rateUnit,reactionRule); +// +// //add probability to function or constant +// varHash.addVariable(newFunctionOrConstant(getMathSymbol(reverse_probParm,geometryClass),getIdentifierSubstitutions(reverse_rateExp, reverse_rateUnit, geometryClass),geometryClass)); +// +// // add reverse ParticleJumpProcess +// Expression reverse_rate = getIdentifierSubstitutions(new Expression(reverse_probParm,getNameScope()), reverse_probParm.getUnitDefinition(), geometryClass); +// String reverse_name = reactionRuleName+"_reverse"; +// JumpProcessRateDefinition reverse_rateDefinition = new MacroscopicRateConstant(reverse_rate); +// ParticleJumpProcess reverse_particleJumpProcess = new ParticleJumpProcess(reverse_name,productParticles,reverse_rateDefinition,reverseActions); +// subDomain.addParticleJumpProcess(reverse_particleJumpProcess); +// +// } +// } + + private Map jumpProcessMap = new LinkedHashMap(); + private void addStrictMassActionParticleJumpProcess(VariableHash varHash, GeometryClass geometryClass, SubDomain subDomain, + ReactionRule reactionRule, String jpName, + Subtype subtype, TransitionCondition transitionCondition, double bondLength, + ArrayList reactantParticles, ArrayList productParticles, + ArrayList forwardActions, ArrayList reverseActions) + throws ExpressionException, ExpressionBindingException, PropertyVetoException, MathException, MappingException { + + String reactionRuleName = reactionRule.getName(); + RbmKineticLaw kinetics = reactionRule.getKineticLaw(); + // + // TODO: it's RulebasedTransformation for now + // + RulebasedTransformation ruleBasedTransformation = ((RulebasedTransformation)getTransformation()); + + if (kinetics.getRateLawType() != RbmKineticLaw.RateLawType.MassAction){ + throw new RuntimeException("expecting mass action kinetics for reaction rule "+reactionRuleName); + } + + // + // construct stochastic forward or reverse rate expression (separately). Transform from + // original expression of "concentrationRate" in terms of rateParameter and reactants/products in concentrations + // to + // new stochastic expression of "molecularRate" in terms of forwardRateParameter, reactants/products in molecules, structure sizes, and unit conversions. + // + // (1) concentrationRate = K * [s0] * [s1] [uM.s-1] or [molecules.um-3.s-1] or [molecules.um-2.s-1] (or other) + // (2) molecularRate = P * * [molecules.s-1] + // + // in this math description, we are using [molecules], but original kinetics were in [s_i] [uM or molecules.um-2]. + // so through a change in variable to get things in terms of . <<<< Here P is the desired stochastic rate coefficient. >>> + // + // (3) let [s_i] = /structsize(s_i)*unitConversionFactor(substanceunit([s_i])/substanceunit()) + // + // in addition to the change in variables, we need to transform the entire expression from concentration/time to molecules/time + // + // (4) let molecularRate = concentrationRate * structSize(reaction) * unitConversionFactor(substanceunit(molecularRate)/substanceunit(concentrationRate)) + // + // (5) in general, concentationRate = K * PRODUCT([s_i]) + // + // change of variables into stochastic variables used in MathDescription, substituting (3) into (5) + // + // (6) concentrationRate = K * PRODUCT(/structsize(s_i)*unitConversionFactor(substanceunit([s_i])/substanceunit())) + // + // reordering to separate the sizes, the unit conversions and the + // + // (7) concentrationRate = K * PRODUCT() * PRODUCT(1/structsize(s_i)) * unitConversionFactor(PRODUCT(substanceunit([s_i])/substanceunit())) + // + // combining (4) and (7) + // + // (8) molecularRate = K * PRODUCT() * PRODUCT(1/structsize(s_i)) * unitConversionFactor(PRODUCT(substanceunit([s_i])/substanceunit())) * structSize(reaction) * unitConversionFactor(substanceunit(molecularRate)/substanceunit(concentrationRate)) + // + // collecting terms of sizes and unit conversions + // + // (9) molecularRate = K * PRODUCT() * structSize(reaction) / PRODUCT(structsize(s_i)) * unitConversionFactor(substanceunit(molecularRate)/substanceunit(concentrationRate) * PRODUCT(substanceunit([s_i])/substanceunit())) + // + // (10) molecularRate = K * PRODUCT() * sizeFactor * unitConversionFactor(substanceConversionUnit) + // + // where + // + // (11) sizeFactor = structSize(reaction) / PRODUCT(structsize(s_i)) + // (12) substanceConversionUnit = substanceunit(molecularRate)/substanceunit(concentrationRate) * PRODUCT(substanceunit([s_i])/substanceunit()) + // + // The ParticleJumpCondition wants a single new rate stochastic, P from equation (2). Note that PRODUCT() will be captured separately the the reactantPatterns. + // comparing (2) and (10) we have found P. + // + // (13) P = K * sizeFactor * unitConversionFactor(substanceConversionUnit) + // + // the framework also needs the proper unit for P + // + // (14) Unit(P) = Unit(K) * Unit(sizeFactor) * substanceConversionUnit + // + // + + ModelUnitSystem modelUnitSystem = getSimulationContext().getModel().getUnitSystem(); + VCUnitDefinition stochasticSubstanceUnit = modelUnitSystem.getStochasticSubstanceUnit(); + VCUnitDefinition reactionRuleSubstanceUnit = modelUnitSystem.getSubstanceUnit(reactionRule.getStructure()); + int forwardRuleIndex = 0; + // + // get forward rate parameter and make sure it is constant valued. + // + Parameter forward_rateParameter = kinetics.getLocalParameter(RbmKineticLawParameterType.MassActionForwardRate); + Expression substitutedForwardRate = MathUtilities.substituteModelParameters(forward_rateParameter.getExpression(), reactionRule.getNameScope().getScopedSymbolTable()); + if (!substitutedForwardRate.flatten().isNumeric()){ + throw new MappingException("forward rate constant for reaction rule "+reactionRule.getName()+" is not constant"); + } + // + // create forward sizeExp and forward unitFactor + // + VCUnitDefinition forward_substanceConversionUnit = stochasticSubstanceUnit.divideBy(reactionRuleSubstanceUnit); + VCUnitDefinition forward_sizeFactorUnit = reactionRule.getStructure().getStructureSize().getUnitDefinition(); + Expression forward_sizeFactor = new Expression(reactionRule.getStructure().getStructureSize(),getNameScope()); + for (ReactantPattern reactantPattern : reactionRule.getReactantPatterns()){ + Expression reactantSizeExp = new Expression(reactantPattern.getStructure().getStructureSize(),getNameScope()); + VCUnitDefinition reactantSizeUnit = reactantPattern.getStructure().getStructureSize().getUnitDefinition(); + VCUnitDefinition reactantSubstanceUnit = modelUnitSystem.getSubstanceUnit(reactantPattern.getStructure()); + forward_sizeFactor = Expression.div(forward_sizeFactor,reactantSizeExp); + forward_sizeFactorUnit = forward_sizeFactorUnit.divideBy(reactantSizeUnit); + forward_substanceConversionUnit = forward_substanceConversionUnit.multiplyBy(reactantSubstanceUnit).divideBy(stochasticSubstanceUnit); + } + // simplify sizeFactor (often has size/size/size) + try { + forward_sizeFactor = RationalExpUtils.getRationalExp(forward_sizeFactor).simplifyAsExpression(); + forward_sizeFactor.bindExpression(getSimulationContext().getModel()); + } catch (ParseException e) { + e.printStackTrace(); + } + + // TODO: we need the right expression here, one that would transform user selected units to what the solver expects (standard vcell units) +// Expression forward_rateExp = Expression.mult(getUnitFactor(forward_substanceConversionUnit), new Expression(forward_rateParameter, getNameScope()),forward_sizeFactor).simplifyJSCL(); + Expression forward_rateExp = new Expression(forward_rateParameter, getNameScope()).simplifyJSCL(); + VCUnitDefinition forward_rateUnit = forward_rateParameter.getUnitDefinition().multiplyBy(forward_sizeFactorUnit).multiplyBy(forward_substanceConversionUnit); + + ProbabilityParameter forward_probParm = addProbabilityParameter(PARAMETER_PROBABILITYRATE_PREFIX+jpName, forward_rateExp, PARAMETER_ROLE_P, forward_rateUnit,reactionRule); + //add probability to function or constant + varHash.addVariable(newFunctionOrConstant(getMathSymbol(forward_probParm,geometryClass),getIdentifierSubstitutions(forward_rateExp, forward_rateUnit, geometryClass),geometryClass)); + + // add forward ParticleJumpProcess + String forward_name = reactionRuleName; + Expression forward_rate = getIdentifierSubstitutions(new Expression(forward_probParm,getNameScope()), forward_probParm.getUnitDefinition(), geometryClass); + JumpProcessRateDefinition forward_rateDefinition = new MacroscopicRateConstant(forward_rate); + + ReactionRuleAnalysisReport rrarBiomodelForward = ruleBasedTransformation.getRulesForwardMap().get(reactionRule); + ProcessSymmetryFactor forwardSymmetryFactor = new ProcessSymmetryFactor(rrarBiomodelForward.getSymmetryFactor()); + LangevinParticleJumpProcess forward_particleJumpProcess = new LangevinParticleJumpProcess(forward_name,reactantParticles,forward_rateDefinition,forwardActions,forwardSymmetryFactor); + forward_particleJumpProcess.setSubtype(subtype); + forward_particleJumpProcess.setTransitionCondition(transitionCondition); + forward_particleJumpProcess.setBondLength(bondLength); + subDomain.addParticleJumpProcess(forward_particleJumpProcess); + + // + // verify that map and operations are consistent between BNG generated NFSimXML and RuleAnalysis algorithm. + // + for (ReactionRule rr : getSimulationContext().getModel().getRbmModelContainer().getReactionRuleList()){ + if (rr == reactionRule){ + break; + } + forwardRuleIndex++; + if (rr.isReversible()){ + forwardRuleIndex++; + } + } +// System.out.println("\n\n--------- new rule analysis report for ReactionRule \""+reactionRule.getName()+"\" (#"+(forwardRuleIndex+RuleAnalysis.INDEX_OFFSET)+") -----------\n"); +// ReactionRuleDirection forward = ReactionRuleDirection.forward; +// System.out.println(RbmUtils.toBnglStringShort(reactionRule, CompartmentMode.show)+", direction="+forward); +// +// MathRuleFactory mathRuleFactory = new MathRuleFactory(); +// MathRuleEntry mathRule = mathRuleFactory.createRuleEntry(forward_particleJumpProcess, forwardRuleIndex); +// ((RulebasedTransformation)getTransformation()).compareOutputs(mathRule); // compare the xml from BioNetGen with the one we build +// +// RuleAnalysisReport mathReport = RuleAnalysis.analyze(mathRule, true); +// Set ours = mathReport.getSummaryAsSet(); +// Set theirs = getSummaryAsSet(rrarBiomodelForward); +// if (compareSets(ours, theirs) == true){ +// System.out.println("Rule Analysis SAME\n"); +// }else{ +// System.out.println("Rule Analysis DIFFERENT\n"); +// } + + // + // get reverse rate parameter and make sure it is missing or constant valued. + // + if (reactionRule.isReversible() && Subtype.BINDING == subtype){ + Parameter reverse_rateParameter = kinetics.getLocalParameter(RbmKineticLawParameterType.MassActionReverseRate); + if (reverse_rateParameter==null || reverse_rateParameter.getExpression()==null){ + throw new MappingException("reverse rate constant for reaction rule "+reactionRule.getName()+" is missing"); + } + { + Expression substitutedReverseRate = MathUtilities.substituteModelParameters(reverse_rateParameter.getExpression(), reactionRule.getNameScope().getScopedSymbolTable()); + if (!substitutedReverseRate.flatten().isNumeric()){ + throw new MappingException("reverse rate constant for reaction rule "+reactionRule.getName()+" is not constant"); + } + } + + // + // create reverse sizeExp and reverse unitFactor + // + VCUnitDefinition reverse_substanceConversionUnit = stochasticSubstanceUnit.divideBy(reactionRuleSubstanceUnit); + VCUnitDefinition reverse_sizeFactorUnit = reactionRule.getStructure().getStructureSize().getUnitDefinition(); + Expression reverse_sizeFactor = new Expression(reactionRule.getStructure().getStructureSize(),getNameScope()); + for (ProductPattern productPattern : reactionRule.getProductPatterns()){ + Expression reactantSizeExp = new Expression(productPattern.getStructure().getStructureSize(),getNameScope()); + VCUnitDefinition reactantSizeUnit = productPattern.getStructure().getStructureSize().getUnitDefinition(); + VCUnitDefinition reactantSubstanceUnit = modelUnitSystem.getSubstanceUnit(productPattern.getStructure()); + reverse_sizeFactor = Expression.div(reverse_sizeFactor,reactantSizeExp); + reverse_sizeFactorUnit = reverse_sizeFactorUnit.divideBy(reactantSizeUnit); + reverse_substanceConversionUnit = reverse_substanceConversionUnit.multiplyBy(reactantSubstanceUnit).divideBy(stochasticSubstanceUnit); + } + // simplify sizeFactor (often has size/size/size) + try { + reverse_sizeFactor = RationalExpUtils.getRationalExp(reverse_sizeFactor).simplifyAsExpression(); + reverse_sizeFactor.bindExpression(getSimulationContext().getModel()); + } catch (ParseException e) { + e.printStackTrace(); + } + + Expression reverse_rateExp = Expression.mult(new Expression(reverse_rateParameter, getNameScope()),reverse_sizeFactor,getUnitFactor(reverse_substanceConversionUnit)).simplifyJSCL(); + VCUnitDefinition reverse_rateUnit = reverse_rateParameter.getUnitDefinition().multiplyBy(reverse_sizeFactorUnit).multiplyBy(reverse_substanceConversionUnit); + + // if the reaction has forward rate (Mass action,HMMs), or don't have either forward or reverse rate (some other rate laws--like general) + // we process it as forward reaction + // get jump process name + + ProbabilityParameter reverse_probParm = addProbabilityParameter(PARAMETER_PROBABILITYRATE_PREFIX+jpName+"_reverse", reverse_rateExp, PARAMETER_ROLE_P_reverse, reverse_rateUnit,reactionRule); + + //add probability to function or constant + varHash.addVariable(newFunctionOrConstant(getMathSymbol(reverse_probParm,geometryClass),getIdentifierSubstitutions(reverse_rateExp, reverse_rateUnit, geometryClass),geometryClass)); + + // add reverse ParticleJumpProcess + Expression reverse_rate = getIdentifierSubstitutions(new Expression(reverse_probParm,getNameScope()), reverse_probParm.getUnitDefinition(), geometryClass); + String reverse_name = reactionRuleName+"_reverse"; + JumpProcessRateDefinition reverse_rateDefinition = new MacroscopicRateConstant(reverse_rate); + + ReactionRuleAnalysisReport rrarBiomodelReverse = ruleBasedTransformation.getRulesReverseMap().get(reactionRule); + ProcessSymmetryFactor reverseSymmetryFactor = new ProcessSymmetryFactor(rrarBiomodelReverse.getSymmetryFactor()); + LangevinParticleJumpProcess reverse_particleJumpProcess = new LangevinParticleJumpProcess(reverse_name,productParticles,reverse_rateDefinition,reverseActions,reverseSymmetryFactor); + reverse_particleJumpProcess.setSubtype(subtype); + reverse_particleJumpProcess.setTransitionCondition(transitionCondition); + reverse_particleJumpProcess.setBondLength(bondLength); + subDomain.addParticleJumpProcess(reverse_particleJumpProcess); + + // + // check reverse direction mapping and operations with RuleAnalysis. + // + int reverseRuleIndex = forwardRuleIndex + 1; + ReactionRuleAnalysisReport rrar = ruleBasedTransformation.getRulesReverseMap().get(reactionRule); + jumpProcessMap.put(reverse_particleJumpProcess, rrar); + } + } + + private static boolean compareSets(Set ours, Set theirs) { + for(String our : ours) { + if(!theirs.contains(our)) { + return false; + } + } + for(String their : theirs) { + if(!ours.contains(their)) { + return false; + } + } + return true; + } + private static Set getSummaryAsSet(ReactionRuleAnalysisReport rrar) { + Set summary = new HashSet(); + for (Pair mapping : rrar.getIdMappingList()){ + String str1 = mapping.one; + String str2 = mapping.two; + if(str1 == null) { + summary.add("map "+str2+"\n"); + } else { + summary.add("map "+str2+" to "+str1+"\n"); + } + } + for (Operation op : rrar.getOperationsList()) { + summary.add("operation " + op.toString() + "\n"); + } + return summary; + } + + private List addObservables( + GeometryClass geometryClass, + Domain domain, + HashMap speciesPatternMap) + throws MappingException, MathException { + + ArrayList observables = new ArrayList<>(); + // + // Particle Observables from Observables defined in Model + // + for(MathMappingParameter mathMappingParameter : getMathMappingParameters()) { + if (mathMappingParameter instanceof ObservableCountParameter){ + ObservableCountParameter observableCountParameter = (ObservableCountParameter)mathMappingParameter; + RbmObservable rbmObservable = observableCountParameter.getObservable(); + ParticleObservable.ObservableType particleObservableType = null; + if (rbmObservable.getType() == RbmObservable.ObservableType.Molecules){ + particleObservableType = ParticleObservable.ObservableType.Molecules; + }else{ + particleObservableType = ParticleObservable.ObservableType.Species; + } + ParticleObservable particleObservable = new VolumeParticleObservable(getMathSymbol(observableCountParameter, geometryClass),domain,particleObservableType); + + switch (rbmObservable.getSequence()){ + case Multimolecular:{ + particleObservable.setSequence(Sequence.Multimolecular); + break; + } + case PolymerLengthEqual:{ + particleObservable.setSequence(Sequence.PolymerLengthEqual); + particleObservable.setQuantity(rbmObservable.getSequenceLength()); + break; + } + case PolymerLengthGreater:{ + particleObservable.setSequence(Sequence.PolymerLengthGreater); + particleObservable.setQuantity(rbmObservable.getSequenceLength()); + break; + } + default:{ + throw new RuntimeException("unexpected sequence "+rbmObservable.getSequence()); + } + } + + for(SpeciesPattern speciesPattern : rbmObservable.getSpeciesPatternList()) { + VolumeParticleSpeciesPattern vpsp = speciesPatternMap.get(speciesPattern); + particleObservable.addParticleSpeciesPattern(vpsp); + } + observables.add(particleObservable); + } + if (mathMappingParameter instanceof SpeciesCountParameter){ + SpeciesCountParameter speciesCountParameter = (SpeciesCountParameter)mathMappingParameter; + ParticleObservable.ObservableType particleObservableType = ParticleObservable.ObservableType.Species; + ParticleObservable particleObservable = new VolumeParticleObservable(getMathSymbol(speciesCountParameter, geometryClass),domain,particleObservableType); + particleObservable.setSequence(Sequence.Multimolecular); + SpeciesPattern speciesPattern = speciesCountParameter.getSpeciesContext().getSpeciesPattern(); + VolumeParticleSpeciesPattern vpsp = speciesPatternMap.get(speciesPattern); + particleObservable.addParticleSpeciesPattern(vpsp); + observables.add(particleObservable); + } + } + return observables; + } + + private HashMap addSpeciesPatterns(Domain domain, List rrList, + Map molecularTypeToSpeciesContextSpecMap) throws MathException { + // Particle Molecular Types + // + Model model = getSimulationContext().getModel(); + List observableList = model.getRbmModelContainer().getObservableList(); + List molecularTypeList = model.getRbmModelContainer().getMolecularTypeList(); + for (MolecularType molecularType : molecularTypeList) { + Map mcToLpmc = new LinkedHashMap<> (); + LangevinParticleMolecularType particleMolecularType = new LangevinParticleMolecularType(molecularType.getName()); + SpeciesContextSpec scs = molecularTypeToSpeciesContextSpecMap.get(molecularType); // scs may be null for Sink and Source + for (MolecularComponent molecularComponent : molecularType.getComponentList()) { + String pmcName = molecularComponent.getName(); + String pmcId = particleMolecularType.getName() + "_" + molecularComponent.getName(); + LangevinParticleMolecularComponent particleMolecularComponent = new LangevinParticleMolecularComponent(pmcId, pmcName); + for (ComponentStateDefinition componentState : molecularComponent.getComponentStateDefinitions()){ + ParticleComponentStateDefinition pcsd = particleMolecularComponent.getComponentStateDefinition(componentState.getName()); + if(pcsd == null) { + particleMolecularComponent.addComponentStateDefinition(new ParticleComponentStateDefinition(componentState.getName())); + } + } + if(scs != null) { + Map siteAttributesMap = scs.getSiteAttributesMap(); + MolecularTypePattern mtp = scs.getSpeciesContext().getSpeciesPattern().getMolecularTypePatterns().get(0); // we know it's valid, no null pointer possible + MolecularComponentPattern mcp = mtp.getMolecularComponentPattern(molecularComponent); + SiteAttributesSpec sas = siteAttributesMap.get(mcp); + particleMolecularComponent.setColor(sas.getColor()); + particleMolecularComponent.setLocation(sas.getLocation().getName()); + particleMolecularComponent.setRadius(sas.getRadius()); + particleMolecularComponent.setDiffusionRate(sas.getDiffusionRate()); + mcToLpmc.put(mcp, particleMolecularComponent); + } + particleMolecularType.addMolecularComponent(particleMolecularComponent); + } + if(!molecularType.isAnchorAll()) { + List anchorList = new ArrayList<>(); + for(Structure struct : molecularType.getAnchors()) { + anchorList.add(struct.getName()); + } + particleMolecularType.setAnchorList(anchorList); + } + if(scs != null) { + Set internalLinkSet = scs.getInternalLinkSet(); + if(internalLinkSet == null) { + throw new RuntimeException("LangevinMathMapping: the internal link set cannot be null"); + } + for(MolecularInternalLinkSpec mils : internalLinkSet) { + Pair link = mils.getLink(); + LangevinParticleMolecularComponent one = mcToLpmc.get(link.one); + LangevinParticleMolecularComponent two = mcToLpmc.get(link.two); + Pair pair = new Pair<> (one, two); + particleMolecularType.getInternalLinkSpec().add(pair); + } + } + mathDesc.addParticleMolecularType(particleMolecularType); + } + + // + // Assemble list of all Species Patterns (from observables, reaction rules, and seed species). + // + + LinkedHashMap speciesPatternStructureMap = new LinkedHashMap(); // linked hash set maintains insertion order + for (RbmObservable observable : observableList){ + for (SpeciesPattern speciesPattern : observable.getSpeciesPatternList()){ + speciesPatternStructureMap.put(speciesPattern, observable.getStructure()); + } + } + for (ReactionRule reactionRule : rrList){ + for (ReactantPattern rp : reactionRule.getReactantPatterns()){ + speciesPatternStructureMap.put(rp.getSpeciesPattern(), rp.getStructure()); + } + for (ProductPattern pp : reactionRule.getProductPatterns()){ + speciesPatternStructureMap.put(pp.getSpeciesPattern(), pp.getStructure()); + } + } + for (SpeciesContext sc : model.getSpeciesContexts()){ + if(!sc.hasSpeciesPattern()) { continue; } + speciesPatternStructureMap.put(sc.getSpeciesPattern(), sc.getStructure()); + } + + // + // add list of unique speciesPatterns + // + HashMap speciesPatternVCMLMap = new HashMap(); + HashMap speciesPatternMap = new HashMap(); + String speciesPatternName = "speciesPattern0"; + for (SpeciesPattern speciesPattern : speciesPatternStructureMap.keySet()) { + VolumeParticleSpeciesPattern volumeParticleSpeciesPattern = new VolumeParticleSpeciesPattern(speciesPatternName,domain,speciesPatternStructureMap.get(speciesPattern).getName()); + + for (MolecularTypePattern molecularTypePattern : speciesPattern.getMolecularTypePatterns()){ + ParticleMolecularType particleMolecularType = mathDesc.getParticleMolecularType(molecularTypePattern.getMolecularType().getName()); + if(!(particleMolecularType instanceof LangevinParticleMolecularType)) { + throw new RuntimeException("LangevinMathMapping: particleMolecularType must be instanceof LangevinParticleMolecularType"); + } + ParticleMolecularTypePattern particleMolecularTypePattern = new ParticleMolecularTypePattern(particleMolecularType); + String participantMatchLabel = molecularTypePattern.getParticipantMatchLabel(); + if (molecularTypePattern.getParticipantMatchLabel()!=null){ + particleMolecularTypePattern.setMatchLabel(participantMatchLabel); + } + + for (MolecularComponentPattern molecularComponentPattern : molecularTypePattern.getComponentPatternList()){ + MolecularComponent molecularComponent = molecularComponentPattern.getMolecularComponent(); + ParticleMolecularComponent particleMolecularComponent = particleMolecularType.getMolecularComponent(molecularComponent.getName()); + if(!(particleMolecularComponent instanceof LangevinParticleMolecularComponent)) { + throw new RuntimeException("LangevinMathMapping: particleMolecularComponent must be instanceof LangevinParticleMolecularComponent"); + } + ParticleMolecularComponentPattern particleMolecularComponentPattern = new ParticleMolecularComponentPattern(particleMolecularComponent); + ComponentStatePattern componentState = molecularComponentPattern.getComponentStatePattern(); + if (componentState != null){ + if(componentState.isAny()) { + ParticleComponentStatePattern pcsp = new ParticleComponentStatePattern(); + particleMolecularComponentPattern.setComponentStatePattern(pcsp); + } else { + String name = componentState.getComponentStateDefinition().getName(); + ParticleComponentStateDefinition pcsd = particleMolecularComponent.getComponentStateDefinition(name); + + //ParticleComponentStateDefinition pcsd = new ParticleComponentStateDefinition(componentState.getComponentStateDefinition().getName()); + //particleMolecularComponent.addComponentStateDefinition(pcsd); + ParticleComponentStatePattern pcsp = new ParticleComponentStatePattern(pcsd); + particleMolecularComponentPattern.setComponentStatePattern(pcsp); + } + }else{ + ParticleComponentStatePattern pcsp = new ParticleComponentStatePattern(); + particleMolecularComponentPattern.setComponentStatePattern(pcsp); + } + switch (molecularComponentPattern.getBondType()){ + case Specified:{ + particleMolecularComponentPattern.setBondType(ParticleBondType.Specified); + particleMolecularComponentPattern.setBondId(molecularComponentPattern.getBondId()); + break; + } + case Exists:{ + particleMolecularComponentPattern.setBondType(ParticleBondType.Exists); + particleMolecularComponentPattern.setBondId(-1); + break; + } + case None:{ + particleMolecularComponentPattern.setBondType(ParticleBondType.None); + particleMolecularComponentPattern.setBondId(-1); + break; + } + case Possible:{ + particleMolecularComponentPattern.setBondType(ParticleBondType.Possible); + particleMolecularComponentPattern.setBondId(-1); + break; + } + } + particleMolecularTypePattern.addMolecularComponentPattern(particleMolecularComponentPattern); + } + volumeParticleSpeciesPattern.addMolecularTypePattern(particleMolecularTypePattern); + } + String speciesPatternVCML = volumeParticleSpeciesPattern.getVCML("tempName"); + VolumeParticleSpeciesPattern uniqueVolumeParticleSpeciesPattern = speciesPatternVCMLMap.get(speciesPatternVCML); + if (uniqueVolumeParticleSpeciesPattern==null){ + speciesPatternVCMLMap.put(speciesPatternVCML, volumeParticleSpeciesPattern); + speciesPatternName = TokenMangler.getNextEnumeratedToken(speciesPatternName); + speciesPatternMap.put(speciesPattern,volumeParticleSpeciesPattern); + }else{ + speciesPatternMap.put(speciesPattern,uniqueVolumeParticleSpeciesPattern); + } + } + return speciesPatternMap; + } + +@Override +protected void refreshSpeciesContextMappings() throws ExpressionException, MappingException, MathException +{ + // + // create a SpeciesContextMapping for each speciesContextSpec. + // + // set initialExpression from SpeciesContextSpec. + // set diffusing + // set variable (only if "Constant" or "Function", else leave it as null)-----why commented? + // + + // + // have to put geometric paras into mathsymbolmapping, since species initial condition needs the volume size symbol. + // and the parameters later on were added into contants or functions in refreshMathDescription() + // + StructureMapping structureMappings[] = getSimulationContext().getGeometryContext().getStructureMappings(); + for (int i=0;i enum1 = getSpeciesContextMappings(); + while (enum1.hasMoreElements()){ + SpeciesContextMapping scm = (SpeciesContextMapping)enum1.nextElement(); + SpeciesContextSpec scs = getSimulationContext().getReactionContext().getSpeciesContextSpec(scm.getSpeciesContext()); + //stochastic variable is always a function of size. + SpeciesCountParameter spCountParm = null; + try{ + String countName = scs.getSpeciesContext().getName() + BIO_PARAM_SUFFIX_SPECIES_COUNT; + Expression countExp = new Expression(0.0); + spCountParm = addSpeciesCountParameter(countName, countExp, PARAMETER_ROLE_SPECIES_COUNT, scs.getInitialCountParameter().getUnitDefinition(), scs.getSpeciesContext()); + }catch(PropertyVetoException pve){ + pve.printStackTrace(); + throw new MappingException(pve.getMessage()); + } + + //add concentration of species as MathMappingParameter - this will map to species concentration function + try{ + String concName = scs.getSpeciesContext().getName() + BIO_PARAM_SUFFIX_SPECIES_CONCENTRATION; + Expression concExp = getExpressionAmtToConc(new Expression(spCountParm,getNameScope()), scs.getSpeciesContext().getStructure()); + concExp.bindExpression(this); + addSpeciesConcentrationParameter(concName, concExp, PARAMETER_ROLE_SPECIES_CONCENRATION, scs.getSpeciesContext().getUnitDefinition(), scs.getSpeciesContext()); + }catch(Exception e){ + e.printStackTrace(); + throw new MappingException(e.getMessage()); + } + //we always add variables, all species are independent variables, no matter they are constant or not. + String countMathSymbol = getMathSymbol(spCountParm, getSimulationContext().getGeometryContext().getStructureMapping(scs.getSpeciesContext().getStructure()).getGeometryClass()); + scm.setVariable(new VolumeParticleVariable(countMathSymbol,defaultDomain)); + mathSymbolMapping.put(scm.getSpeciesContext(),scm.getVariable().getName()); + } + + for (RbmObservable observable : simContext.getModel().getRbmModelContainer().getObservableList()){ + //stochastic variable is always a function of size. + ObservableCountParameter observableCountParm = null; + try{ + String countName = observable.getName() + BIO_PARAM_SUFFIX_SPECIES_COUNT; + Expression countExp = new Expression(0.0); + observableCountParm = addObservableCountParameter(countName, countExp, PARAMETER_ROLE_OBSERVABLE_COUNT, simContext.getModel().getUnitSystem().getStochasticSubstanceUnit(), observable); + }catch(PropertyVetoException pve){ + pve.printStackTrace(); + throw new MappingException(pve.getMessage()); + } + + //add concentration of species as MathMappingParameter - this will map to species concentration function + try{ + String concName = observable.getName() + BIO_PARAM_SUFFIX_SPECIES_CONCENTRATION; + Expression concExp = getExpressionAmtToConc(new Expression(observableCountParm,getNameScope()), observable.getStructure()); + concExp.bindExpression(this); + addObservableConcentrationParameter(concName, concExp, PARAMETER_ROLE_OBSERVABLE_CONCENTRATION, observable.getUnitDefinition(), observable); + }catch(Exception e){ + e.printStackTrace(); + throw new MappingException(e.getMessage()); + } +// //we always add variables, all species are independent variables, no matter they are constant or not. +// String countMathSymbol = getMathSymbol(observableCountParm, getSimulationContext().getGeometryContext().getStructureMapping(scs.getSpeciesContext().getStructure()).getGeometryClass()); +// scm.setVariable(new VolumeParticleVariable(countMathSymbol,defaultDomain)); +// mathSymbolMapping.put(observable,scm.getVariable().getName()); + } +} + + +} diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/MolecularInternalLinkSpec.java b/vcell-core/src/main/java/cbit/vcell/mapping/MolecularInternalLinkSpec.java new file mode 100644 index 0000000000..3587f20ea6 --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/mapping/MolecularInternalLinkSpec.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package cbit.vcell.mapping; +import java.awt.Color; +import java.awt.Graphics2D; +import java.io.PrintWriter; +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import cbit.vcell.model.*; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.util.*; +import org.vcell.util.Issue.IssueCategory; +import org.vcell.util.Issue.IssueSource; +import org.vcell.util.IssueContext.ContextType; +import org.vcell.util.document.Identifiable; +import org.vcell.util.document.VersionFlag; + +@SuppressWarnings("serial") +public class MolecularInternalLinkSpec implements Identifiable, IssueSource, Matchable, Serializable { + private final SpeciesContextSpec fieldSpeciesContextSpec; + private MolecularComponentPattern fieldMolecularComponentPatternOne = null; + private MolecularComponentPattern fieldMolecularComponentPatternTwo = null; +// private double linkLength = 0; + + public MolecularInternalLinkSpec(SpeciesContextSpec scs, MolecularComponentPattern linkOne, MolecularComponentPattern linkTwo) throws IllegalArgumentException { + fieldSpeciesContextSpec = scs; + SpeciesContext sc = scs.getSpeciesContext(); + SpeciesPattern sp = sc.getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns().size() != 1) { + throw new IllegalArgumentException("The species pattern must contain exactly one molecule."); + } + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); // the one and only + // sanity check + if(linkOne == null || linkOne.getMolecularComponent() == null) { + throw new IllegalArgumentException("A link component doesn't exist"); + } + if(linkTwo == null || linkTwo.getMolecularComponent() == null) { + throw new IllegalArgumentException("A link component doesn't exist"); + } + boolean foundOne = false; + boolean foundTwo = false; + for(MolecularComponentPattern mpc : mtp.getComponentPatternList()) { + if(mpc == linkOne) { + foundOne = true; + } + } + for(MolecularComponentPattern mpc : mtp.getComponentPatternList()) { + if(mpc == linkTwo) { + foundTwo = true; + } + } + if(!foundOne || !foundTwo) { + throw new IllegalArgumentException("A link component doesn't match any molecule component"); + } + // order them based on position in the Molecule + // we may only have one molecule in the species pattern for the current version of springsalad impl, but who knows in the future + for(MolecularComponentPattern mpc : mtp.getComponentPatternList()) { + if(mpc == linkOne) { // linkOne comes first + fieldMolecularComponentPatternOne = linkOne; + fieldMolecularComponentPatternTwo = linkTwo; + break; + } else if(mpc == linkTwo) { // linkTwo comes first + fieldMolecularComponentPatternOne = linkTwo; + fieldMolecularComponentPatternTwo = linkOne; + break; + } + } + } + public SpeciesContextSpec getSpeciesContextSpec() { + return fieldSpeciesContextSpec; + } + public MolecularComponentPattern getMolecularComponentPatternOne() { + return fieldMolecularComponentPatternOne; + } + public MolecularComponentPattern getMolecularComponentPatternTwo() { + return fieldMolecularComponentPatternTwo; + } + public SiteAttributesSpec getSite1() { + return getSpeciesContextSpec().getSiteAttributesMap().get(fieldMolecularComponentPatternOne); + } + public SiteAttributesSpec getSite2() { + return getSpeciesContextSpec().getSiteAttributesMap().get(fieldMolecularComponentPatternTwo); + } + + public double getX1() { + return getSite1().getX(); + } + public double getY1() { + return getSite1().getY(); + } + public double getZ1() { + return getSite1().getZ(); + } + public double getX2() { + return getSite2().getX(); + } + public double getY2() { + return getSite2().getY(); + } + public double getZ2() { + return getSite2().getZ(); + } + public double getLinkLength() { + double dx = getX2() - getX1(); + double dy = getX2() - getX1(); + double dz = getX2() - getX1(); + double linkLength = Math.sqrt(dx*dx + dy*dy + dz*dz); + return linkLength; + } + public double [] unitVector() { + double dx = getX2() - getX1(); + double dy = getX2() - getX1(); + double dz = getX2() - getX1(); + double length = Math.sqrt(dx*dx + dy*dy + dz*dz); + return new double[]{dx/length, dy/length, dz/length}; + } + + // The conversion factor between pixels and nanometers + // TODO: move this to some other class if appropriate + public static final int PIXELS_PER_NM = 20; + public static final double NM_PER_PIXEL = 0.05; + /** + * Given a line defined by the formula Ax + By + C = 0 and a point + * (x0, y0), the shortest distance between the point and the line + * is given by r = (A x0 + B y0 + C) / sqrt(A^2 + B^2). Also, given + * two points (x1, y1) and (x2, y2), they define a line with + * A = y1-y2, B = x2-x1, and C = x1 y2 - x2 y1 . So we'll take our sites + * to find A, B, and C, and then use these in combination with the + * mouse click to find the distance between the mouse click and + * the line. + */ + public boolean contains(int px, int py, int pz) { + boolean in = false; + int pixelsPerNm = PIXELS_PER_NM; + int z1 = (int)(pixelsPerNm*getZ1()); + int z2 = (int)(pixelsPerNm*getZ2()); + int y1 = (int)(pixelsPerNm*getY1()); + int y2 = (int)(pixelsPerNm*getY2()); + if(z1 > z2) { + int tempx = z2; + int tempy = y2; + z2 = z1; + y2 = y1; + z1 = tempx; + y1 = tempy; + } + int A = y1 - y2; + int B = z2 - z1; + int C = z1 * y2 - z2 * y1; + // Check to make sure we're in the right x range + if(z1 < pz && pz < z2) { + // Calculate distance to line + double r = Math.abs(A * pz + B * py + C)/Math.sqrt(A*A + B*B); + if(r < 3) { + in = true; + } + } + return in; + } + + + public Pair getLink() { + return new Pair(fieldMolecularComponentPatternOne, fieldMolecularComponentPatternTwo); + } + public static Pair getLink(MolecularInternalLinkSpec internalLink) { + if(internalLink == null) { + return null; + } + return new Pair(internalLink.fieldMolecularComponentPatternOne, internalLink.fieldMolecularComponentPatternTwo); + } + @Override + public boolean compareEqual(Matchable obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof MolecularInternalLinkSpec)) { + return false; + } + MolecularInternalLinkSpec candidate = (MolecularInternalLinkSpec)obj; + if(fieldSpeciesContextSpec != candidate.getSpeciesContextSpec()) { + return false; + } + if((fieldMolecularComponentPatternOne == candidate.fieldMolecularComponentPatternOne) && + (fieldMolecularComponentPatternTwo == candidate.fieldMolecularComponentPatternTwo)) { + return true; + } + return false; + } + + public void draw(Graphics2D g2){ + g2.setColor(Color.black); + int pixelsPerNm = PIXELS_PER_NM; + // Map site z to drawPanel x + int x1 = (int)(pixelsPerNm*getZ1()); + int x2 = (int)(pixelsPerNm*getZ2()); + int y1 = (int)(pixelsPerNm*getY1()); + int y2 = (int)(pixelsPerNm*getY2()); + g2.drawLine(x1, y1, x2, y2); + } + + public void writeLink(StringBuilder sb) { + if(getSite1() == null) { + System.out.println("Site 1 is null."); + } + if(getSite2() == null) { + System.out.println("Site 2 is null."); + } + sb.append("LINK: Site " + getSite1().getIndex() + " ::: Site " + getSite2().getIndex()); + sb.append("\n"); + } + + + +// // TODO: not working properly, will use direct calls from SpeciesContextSpec +// public void gatherIssues(IssueContext issueContext, List issueVector) { +// issueContext = issueContext.newChildContext(ContextType.MolecularInternalLinkSpec, this); +// if(fieldMolecularComponentPatternOne == fieldMolecularComponentPatternTwo) { +// String msg = "A generic issue for MolecularInternalLinkSpec entity."; +// String tip = "Both sites of the Link are identical."; +// issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); +// } +// } + +} diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/ReactionContext.java b/vcell-core/src/main/java/cbit/vcell/mapping/ReactionContext.java index 8710022c13..ac99215215 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/ReactionContext.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/ReactionContext.java @@ -20,12 +20,14 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.vcell.model.rbm.MolecularType; import org.vcell.util.BeanUtils; import org.vcell.util.Compare; import org.vcell.util.Issue; import org.vcell.util.IssueContext; import org.vcell.util.Matchable; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; import cbit.vcell.model.Model; import cbit.vcell.model.Model.ModelParameter; @@ -285,15 +287,26 @@ public synchronized boolean hasListeners(java.lang.String propertyName) { * and the property that has changed. */ public void propertyChange(java.beans.PropertyChangeEvent evt) { - if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals("reactionSteps")){ + if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals(Model.PROPERTY_NAME_REACTION_STEPS)) { refreshAll(); } if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals(RbmModelContainer.PROPERTY_NAME_REACTION_RULE_LIST)){ refreshAll(); } - if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals("speciesContexts")){ + if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals(RbmModelContainer.PROPERTY_NAME_MOLECULAR_TYPE_LIST)){ + ; // do nothing + } + if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals(Model.PROPERTY_NAME_SPECIES_CONTEXTS)){ refreshAll(); } + if (evt.getSource().equals(getModel()) && evt.getPropertyName().equals(MolecularType.PROPERTY_NAME_COMPONENT_LIST)) { + if(evt.getOldValue() instanceof MolecularType) { + refreshSpeciesContextSpecs((MolecularType)evt.getOldValue()); + } else { + throw new RuntimeException("evt.getOldValue() must be a MolecularType"); + } + + } } private void refreshAll() @@ -605,6 +618,19 @@ private void refreshSpeciesContextSpecs() throws MappingException { throw new MappingException(e.getMessage(), e); } } + if(Application.SPRINGSALAD == simContext.getApplicationType()) { + for(SpeciesContextSpec scs : fieldSpeciesContextSpecs) { + scs.initializeForSpringSaLaD(null); + } + } +} +private void refreshSpeciesContextSpecs(MolecularType mt) { + if(Application.SPRINGSALAD == simContext.getApplicationType()) { + for(SpeciesContextSpec scs : fieldSpeciesContextSpecs) { + scs.initializeForSpringSaLaD(mt); + } + } + } diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/ReactionRuleSpec.java b/vcell-core/src/main/java/cbit/vcell/mapping/ReactionRuleSpec.java index 6c485f089c..45dee573ef 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/ReactionRuleSpec.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/ReactionRuleSpec.java @@ -9,16 +9,43 @@ */ package cbit.vcell.mapping; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import org.vcell.model.rbm.ComponentStateDefinition; +import org.vcell.model.rbm.ComponentStatePattern; +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.model.rbm.MolecularComponentPattern.BondType; +import org.vcell.model.rbm.SpeciesPattern.Bond; +import org.vcell.model.rbm.MolecularType; import org.vcell.util.Issue; +import org.vcell.util.Issue.IssueCategory; import org.vcell.util.Issue.IssueSource; +import org.vcell.util.document.BioModelChildSummary.MathType; import org.vcell.util.IssueContext; import org.vcell.util.Matchable; +import org.vcell.util.Pair; +import cbit.vcell.mapping.ParameterContext.LocalParameter; +import cbit.vcell.mapping.SimulationContext.SimulationContextParameter; +import cbit.vcell.model.Feature; +import cbit.vcell.model.ProductPattern; +import cbit.vcell.model.RbmKineticLaw; +import cbit.vcell.model.ReactantPattern; import cbit.vcell.model.ReactionRule; +import cbit.vcell.model.ReactionRuleParticipant; +import cbit.vcell.model.SpeciesContext; +import cbit.vcell.model.Structure; +import cbit.vcell.model.Structure.SpringStructureEnum; +import cbit.vcell.parser.Expression; -public class ReactionRuleSpec implements ModelProcessSpec { +public class ReactionRuleSpec implements ModelProcessSpec, IssueSource { private ReactionRule reactionRule = null; public enum ReactionRuleMappingType { @@ -32,15 +59,12 @@ public enum ReactionRuleMappingType { this.displayName = displayName; this.databaseName = databaseName; } - public String getDisplayName(){ return this.displayName; } - public String getDatabaseName() { return this.databaseName; } - public static ReactionRuleMappingType fromDatabaseName(String databaseName){ for (ReactionRuleMappingType t : values()){ if (t.getDatabaseName().equals(databaseName)){ @@ -49,8 +73,36 @@ public static ReactionRuleMappingType fromDatabaseName(String databaseName){ } return null; } + } + + public static final String ANY_STATE = "Any_State"; // SpringSaLaD stuff + private double fieldBondLength = 1; + public enum Subtype { + INCOMPATIBLE("Not Compatible"), + CREATION("Creation"), + DECAY("Decay"), + TRANSITION("Transition"), + ALLOSTERIC("Allosteric"), + BINDING("Binding"); + final public String columnName; // column name in ModelProcessSpecsTableModel + private Subtype(String columnName) { + this.columnName = columnName; + } } + public enum TransitionCondition { // everywhere internally in vcell we use RBM bond type naming conventions + NONE("Unbound", "None"), // MolecularComponentPattern.BondType.None (-) + FREE("Any", "Free"), // MolecularComponentPattern.BondType.Possible (?) + BOUND("Bound", "Bound"); // MolecularComponentPattern.BondType.Exists (+) + + final public String vcellName; + final public String lngvName; + private TransitionCondition(String vcellName, String lngvName) { + this.vcellName = vcellName; + this.lngvName = lngvName; + } + } + protected transient java.beans.PropertyChangeSupport propertyChange; private ReactionRuleMappingType fieldReactionRuleMapping = ReactionRuleMappingType.INCLUDED; @@ -75,13 +127,13 @@ public ReactionRuleSpec(ReactionRuleSpec argReactionRuleSpec) { setReactionRule(argReactionRuleSpec.reactionRule); this.fieldReactionRuleMapping = argReactionRuleSpec.fieldReactionRuleMapping; refreshDependencies(); - } + } public ReactionRuleSpec(ReactionRule argReactionRule) { setReactionRule(argReactionRule); refreshDependencies(); - } + } /** @@ -98,21 +150,17 @@ public synchronized void addPropertyChangeListener(java.beans.PropertyChangeList * @param object java.lang.Object */ public boolean compareEqual(Matchable object) { - ReactionRuleSpec reactionSpec = null; - if (!(object instanceof ReactionRuleSpec)){ + if (!(object instanceof ReactionRuleSpec)) { return false; } reactionSpec = (ReactionRuleSpec)object; - - if (!reactionRule.compareEqual(reactionSpec.reactionRule)){ + if (!reactionRule.compareEqual(reactionSpec.reactionRule)) { return false; } - - if (fieldReactionRuleMapping != reactionSpec.fieldReactionRuleMapping){ + if (fieldReactionRuleMapping != reactionSpec.fieldReactionRuleMapping) { return false; } - return true; } @@ -124,16 +172,697 @@ public void firePropertyChange(String propertyName, Object oldValue, Object newV getPropertyChange().firePropertyChange(propertyName,oldValue,newValue); } +public static final String WrongNumberOfMolecules = "wrongNumberOfMolecules"; +public static final String NumReactants = "numReactants"; +public static final String NumProducts = "numProducts"; +public static final String MtpReactantState = "mtpReactantState"; // the mtp with a state change +public static final String McpReactantState = "mcpReactantState"; // the site where the state changes +public static final String MtpProductState = "mtpProductState"; +public static final String McpProductState = "mcpProductState"; +public static final String MtpReactantBond = "mtpReactantBond"; // the mtp with a binding transition +public static final String McpReactantBond = "mcpReactantBond"; // the site that is bonding +public static final String StateTransitionCounter = "stateTransitionCounter"; +public static final String BondTransitionCounter = "bondTransitionCounter"; +public static final String CspReactantBond = "cspReactantBond"; + +// -------------------------------------------------------------------------------------------------------- +public void analizeReaction(Map analysisResults) { + List rpList = reactionRule.getReactantPatterns(); + List ppList = reactionRule.getProductPatterns(); + // sanity check; get out of here fast if basic conditions are not satisfied + // TODO: move some tests in issues code maybe + analysisResults.put(WrongNumberOfMolecules, true); + boolean wrongNumberOfReactantsProducts = false; + if(rpList == null || rpList.isEmpty()) { + analysisResults.put(NumReactants, 0); + wrongNumberOfReactantsProducts = true; + } + if(ppList == null || ppList.isEmpty()) { + analysisResults.put(NumProducts, 0); + wrongNumberOfReactantsProducts = true; + } + if(rpList.size() > 2) { + analysisResults.put(NumReactants, rpList.size()); + wrongNumberOfReactantsProducts = true; + } + if(ppList.size() > 2) { + analysisResults.put(NumProducts, ppList.size()); + wrongNumberOfReactantsProducts = true; + } + if(wrongNumberOfReactantsProducts) { + return; + } + analysisResults.put(NumReactants, rpList.size()); + analysisResults.put(NumProducts, ppList.size()); + + for(ReactantPattern rp : rpList) { + SpeciesPattern sp = rp.getSpeciesPattern(); + if(sp == null) { + return; + } + List mtpList = sp.getMolecularTypePatterns(); + if(mtpList == null || mtpList.isEmpty() || mtpList.size() > 2) { // each reactant may have 1 or 2 molecule + return; + } + } + for(ProductPattern pp : ppList) { + SpeciesPattern sp = pp.getSpeciesPattern(); + if(sp == null) { + return; + } + List mtpList = sp.getMolecularTypePatterns(); + if(mtpList == null || mtpList.isEmpty() || mtpList.size() > 2) { // each product may have 1 or 2 molecule + return; + } + } + + analysisResults.put(WrongNumberOfMolecules, false); + // looks expensive but a springsalad binding reaction has exactly 2 reactants one molecule each + // and exactly one product made of 2 molecules. Most other springsalad reactions are even simpler + // since instances of mtp / mcp are different between reactants and products, we work with instances of mc / mp + Map reactantToProductSitesMap = new LinkedHashMap<> (); + Map productToReactantSitesMap = new LinkedHashMap<> (); + Map siteToMoleculeMap = new LinkedHashMap<> (); + + // map each reactant site to corresponding product site and vice-versa, map each site to its molecule + for(ReactantPattern rp : rpList) { + for(ProductPattern pp : ppList) { + SpeciesPattern spReactant = rp.getSpeciesPattern(); + SpeciesPattern spProduct = pp.getSpeciesPattern(); + List mtpReactantList = spReactant.getMolecularTypePatterns(); + List mtpProductList = spProduct.getMolecularTypePatterns(); + for(MolecularTypePattern mtpReactant : mtpReactantList) { + for(MolecularTypePattern mtpProduct : mtpProductList) { + MolecularType mtProduct = mtpProduct.getMolecularType(); + MolecularType mtReactant = mtpReactant.getMolecularType(); + String pmlProduct = mtpProduct.getParticipantMatchLabel(); + String pmlReactant = mtpReactant.getParticipantMatchLabel(); + if(mtProduct == mtReactant && pmlProduct.equals(pmlReactant)) { + // bingo, matched a reactant with a product + List mcpReactantList = mtpReactant.getComponentPatternList(); + List mcpProductList = mtpProduct.getComponentPatternList(); + if(mcpReactantList == null || mcpProductList == null || mcpReactantList.isEmpty() || mcpProductList.isEmpty()) { + return; // no components in some molecule, need at least one + } + for(MolecularComponentPattern mcpReactant : mcpReactantList) { + MolecularComponentPattern mcpProduct = mtpProduct.getMolecularComponentPattern(mcpReactant.getMolecularComponent()); + reactantToProductSitesMap.put(mcpReactant, mcpProduct); + productToReactantSitesMap.put(mcpProduct, mcpReactant); + siteToMoleculeMap.put(mcpReactant, mtpReactant); + siteToMoleculeMap.put(mcpProduct, mtpProduct); + } + } + } + } + } + } + + int mcpCount = 0; // index in the for loop + int stateTransitionCounter = 0; // number of state changes between reactant site anc corresponding product site + int bondTransitionCounter = 0; // number of times when an unbound site becomes bound + for(Map.Entry entry : productToReactantSitesMap.entrySet()) { + MolecularComponentPattern mcpProduct = entry.getKey(); + MolecularComponentPattern mcpReactant = entry.getValue(); + + ComponentStatePattern cspReactant = mcpReactant.getComponentStatePattern(); + ComponentStatePattern cspProduct = mcpProduct.getComponentStatePattern(); + if(cspReactant != null) { + if(!cspReactant.compareEqual(cspProduct)) { + stateTransitionCounter++; // state changed between corresponding reactant and product sites + String mtpReactantKey = MtpReactantState + stateTransitionCounter; + String mcpReactantKey = McpReactantState + stateTransitionCounter; + String mtpProductKey = MtpProductState + stateTransitionCounter; + String mcpProductKey = McpProductState + stateTransitionCounter; + MolecularTypePattern mtpReactant = siteToMoleculeMap.get(mcpReactant); + MolecularTypePattern mtpProduct = siteToMoleculeMap.get(mcpProduct); + analysisResults.put(mtpReactantKey, mtpReactant); + analysisResults.put(mcpReactantKey, mcpReactant); + analysisResults.put(mtpProductKey, mtpProduct); + analysisResults.put(mcpProductKey, mcpProduct); + } + } + + if(mcpReactant.getBondType() == BondType.None && mcpProduct.getBondType() == BondType.Specified) { + MolecularTypePattern mtpReactant = siteToMoleculeMap.get(mcpProduct); + bondTransitionCounter++; + String mtpKey = MtpReactantBond + bondTransitionCounter; + String mcpKey = McpReactantBond + bondTransitionCounter; + analysisResults.put(mtpKey, mtpReactant); + analysisResults.put(mcpKey, mcpReactant); + + String stateReactant; + if(cspReactant == null) { + stateReactant = "ERROR"; + } else if(cspReactant.isAny()) { + stateReactant = ANY_STATE; + } else { + ComponentStateDefinition csdReactant = cspReactant.getComponentStateDefinition(); + stateReactant = csdReactant.getName(); + } + String cspKey = CspReactantBond + bondTransitionCounter; + analysisResults.put(cspKey, stateReactant); + } + mcpCount++; + } + analysisResults.put(StateTransitionCounter, stateTransitionCounter); + analysisResults.put(BondTransitionCounter, bondTransitionCounter); +} +public Subtype getSubtype(Map analysisResults) { + if(isCreationReaction(analysisResults) == true) { + return Subtype.CREATION; + } + if(isDecayReaction(analysisResults) == true) { + return Subtype.DECAY; + } + if(isTransitionReaction(analysisResults) == true) { + return Subtype.TRANSITION; + } + if(isAllostericReaction(analysisResults) == true) { + return Subtype.ALLOSTERIC; + } + if(isBindingReaction(analysisResults) == true) { + return Subtype.BINDING; + } + return Subtype.INCOMPATIBLE; +} +public TransitionCondition getTransitionCondition(Map analysisResults) { + Object ret = analysisResults.get(WrongNumberOfMolecules); + if(ret == null || (boolean)ret == true) { + return null; + } + ret = analysisResults.get(StateTransitionCounter); + if(ret == null || (int)ret != 1) { + return null; // we need exactly 1 state transition + } + ret = analysisResults.get(BondTransitionCounter); + if(ret == null || (int)ret != 0) { + return null; // no binding allowed + } + if(analysisResults.get(NumReactants) == null || analysisResults.get(NumProducts) == null) { + return null; + } + int numReactants = (int)analysisResults.get(NumReactants); + int numProducts = (int)analysisResults.get(NumProducts); + if(numReactants != 1 || numProducts != 1) { + return null; // we may only have 1 reactant and 1 product + } + if((numReactants == 1 && numProducts != 1) || (numReactants == 2 && numProducts != 2)) { + return null; // equal number of reactant and products, either 1 of each or 2 of each + } + List rpList = reactionRule.getReactantPatterns(); // we already know that this list is not empty + List ppList = reactionRule.getProductPatterns(); + List mtpReactantList = rpList.get(0).getSpeciesPattern().getMolecularTypePatterns(); + List mtpProductList = ppList.get(0).getSpeciesPattern().getMolecularTypePatterns(); + if((mtpReactantList.size() == 1 && mtpProductList.size() != 1) || (mtpReactantList.size() == 2 && mtpProductList.size() != 2)) { + return null; // equal number of molecules in the reactant and product, either 1 of each or 2 of each + } + + if(mtpReactantList.size() == 1 && mtpProductList.size() == 1) { // check if it's a simple transition (Condition "None" or "Free") + int numAnyStates = 0; + int numBondsPossible = 0; + int numBondsUnbound = 0; + MolecularTypePattern mtpReactant = mtpReactantList.get(0); + for(MolecularComponentPattern mcpReactant : mtpReactant.getComponentPatternList()) { + ComponentStatePattern cspReactant = mcpReactant.getComponentStatePattern(); + BondType bondTypeReactant = mcpReactant.getBondType(); + if(cspReactant == null) { + return null; // all sites must have at least one state + } + if(!cspReactant.isAny() && BondType.Possible != bondTypeReactant) { + // this is the transition site, bond must be "Possible" + return null; + } + if(!cspReactant.isAny()) { + ; + } else { + numAnyStates++; + } + if(BondType.Possible == bondTypeReactant) { + numBondsPossible++; + } else if(BondType.None == bondTypeReactant) { + numBondsUnbound++; + } else { + return null; // all bonds must be either none or possible + } + } + int numSites = mtpReactant.getComponentPatternList().size(); + if(numBondsUnbound > 0 && numBondsPossible == 1 && numBondsUnbound == numSites-1 && numAnyStates == numSites-1) { + // transition site is bond Possible and in explicit state + // all the other sites are unbound AND in "Any" state + return TransitionCondition.NONE; // condition is "None" + } else if(numBondsUnbound == 0 && numBondsPossible == numSites && numAnyStates == numSites-1) { + // transition site is bond Possible and in explicit state + // all the other sites are bond Possible AND in "Any" state + return TransitionCondition.FREE; // transition reaction condition is "Free" + } + return null; + } else if(mtpReactantList.size() == 2 && mtpProductList.size() == 2) { // we look for transition with "Bound" condition, means 2 molecules + MolecularTypePattern mtpTransitionReactant = (MolecularTypePattern)analysisResults.get(MtpReactantState + "1"); // the transitioning molecule + int totalExistsBonds = 0; + int totalExplicitStates = 0; + int numExplicitStates[] = { 0, 0}; + int numExistsBonds[] = { 0, 0 }; + for(int i=0; i < mtpReactantList.size(); i++) { + MolecularTypePattern mtpReactant = mtpReactantList.get(i); + List mcpList = mtpReactant.getComponentPatternList(); + if(mcpList == null || mcpList.isEmpty()) { + return null; + } + for(MolecularComponentPattern mcpReactant : mtpReactant.getComponentPatternList()) { + ComponentStatePattern cspReactant = mcpReactant.getComponentStatePattern(); + if(cspReactant == null) { + return null; // all sites must have at least one state + } + BondType bondTypeReactant = mcpReactant.getBondType(); + if(BondType.Exists == bondTypeReactant) { + if(mtpTransitionReactant == mtpReactant) { + // the transition site must be in one molecule, the condition bound site must be in the other + return null; + } + numExistsBonds[i]++; // the condition binding site has bond type "Exists" + totalExistsBonds++; + } else if(BondType.Possible != bondTypeReactant) { + return null; // all other bonds in the reactant must be of type "Possible" + // we know that the 2 molecules are bound somehow, because they are in the same species + // but we don't care between which sites the bond is + } + if(cspReactant.isAny()) { + continue; + } else { + // the site with explicit state must be the same that has bond type exists + if(BondType.Exists != bondTypeReactant && mtpTransitionReactant != mtpReactant) { + return null; + } + } + numExplicitStates[i]++; + totalExplicitStates++; + } + } + + if(totalExistsBonds != 1) { + return null; // the condition bound site is the only one with BondType "exists" + } + if(totalExplicitStates < 1 || totalExplicitStates > 2) { + // transitioning site must be in explicit state + // the condition binding site may be explicit or any + // all the other sites must be in "Any" state + return null; + } + if(numExplicitStates[0] == 1 && numExplicitStates[1] == 1) { + // above we made sure that the transition molecule and the condition molecule are not the same + return TransitionCondition.BOUND; // must have exactly one explicit state in each molecule + } else if(numExplicitStates[0] == 0 && numExplicitStates[1] == 1 && numExistsBonds[0] == 1) { + // this else-if and the next: the only explicit site is the one transitioning + // the conditional site type may also be in the "any" state but must have bond type "Exists" + return TransitionCondition.BOUND; + } else if(numExplicitStates[0] == 1 && numExplicitStates[1] == 0 && numExistsBonds[1] == 1) { + return TransitionCondition.BOUND; + } + } + return null; +} +private boolean isCreationReaction(Map analysisResults) { + List rpList = reactionRule.getReactantPatterns(); + if(rpList.size() == 1) { + SpeciesPattern sp = rpList.get(0).getSpeciesPattern(); + if(sp.getMolecularTypePatterns().size() == 1) { + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); + if(mtp.getComponentPatternList().size() == 0 && SpeciesContextSpec.SourceMoleculeString.equals(mtp.getMolecularType().getName())) { + return true; + } + } + } + return false; +} +private boolean isDecayReaction(Map analysisResults) { + List ppList = reactionRule.getProductPatterns(); + if(ppList.size() == 1) { + SpeciesPattern sp = ppList.get(0).getSpeciesPattern(); + if(sp.getMolecularTypePatterns().size() == 1) { + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); + if(mtp.getComponentPatternList().size() == 0 && SpeciesContextSpec.SinkMoleculeString.equals(mtp.getMolecularType().getName())) { + return true; + } + } + } + return false; +} +private boolean isTransitionReaction(Map analysisResults) { + TransitionCondition tc = getTransitionCondition(analysisResults); + if(tc instanceof TransitionCondition) { + return true; + } + return false; +} +private boolean isAllostericReaction(Map analysisResults) { + Object ret = analysisResults.get(WrongNumberOfMolecules); + if(ret == null || (boolean)ret == true) { + return false; + } + ret = analysisResults.get(StateTransitionCounter); + if(ret == null || (int)ret != 1) { + return false; // we need exactly 1 state transition + } + ret = analysisResults.get(BondTransitionCounter); + if(ret == null || (int)ret != 0) { + return false; // no binding allowed + } + ret = analysisResults.get(NumReactants); + if(ret == null || (int)ret != 1) { + return false; // exactly 1 reactant + } + ret = analysisResults.get(NumProducts); + if(ret == null || (int)ret != 1) { + return false; // exactly 1 product + } + List rpList = reactionRule.getReactantPatterns(); // we already know that this list is not empty + List ppList = reactionRule.getProductPatterns(); + List mtpReactantList = rpList.get(0).getSpeciesPattern().getMolecularTypePatterns(); + List mtpProductList = ppList.get(0).getSpeciesPattern().getMolecularTypePatterns(); + if(mtpReactantList.size() != 1 || mtpProductList.size() != 1) { + return false; + } + MolecularTypePattern mtpReactant = mtpReactantList.get(0); + MolecularTypePattern mtpProduct = mtpProductList.get(0); + int reactantExplicitStates = 0; + int productExplicitStates = 0; + + Set explicitReactantStatesSet = new LinkedHashSet<> (); + List mcpReactantList = mtpReactant.getComponentPatternList(); + List mcpProductList = mtpProduct.getComponentPatternList(); + for(MolecularComponentPattern mcp : mcpReactantList) { + if(mcp.getBondType() != BondType.Possible) { + return false; // by convention, all bonds must be set to "Possible" + } + } + for(MolecularComponentPattern mcp : mcpReactantList) { + if(mcp.getComponentStatePattern() == null) { + return false; // all sites must have at least one state + } + if(!mcp.getComponentStatePattern().isAny()) { + explicitReactantStatesSet.add(mcp.getComponentStatePattern().getComponentStateDefinition()); + reactantExplicitStates++; + } + } + int matchedStates = 0; + int unmatchedStates = 0; + for(MolecularComponentPattern mcp : mcpProductList) { + if(!mcp.getComponentStatePattern().isAny()) { + ComponentStateDefinition csdProductExplicit = mcp.getComponentStatePattern().getComponentStateDefinition(); + if(explicitReactantStatesSet.contains(csdProductExplicit)) { + matchedStates++; + } else { + unmatchedStates++; + } + productExplicitStates++; + } + } + if(matchedStates != 1 || unmatchedStates != 1) { + // the reactant condition state must match the product, the transition state must not match, so one of each + return false; + } + if(reactantExplicitStates == 2 && productExplicitStates == 2) { + return true; // we need 2 explicit states: one that is transitioning and one for the allosteric condition + } + return false; +} +private boolean isBindingReaction(Map analysisResults) { + Object ret = analysisResults.get(WrongNumberOfMolecules); + if(ret == null || (boolean)ret == true) { + return false; + } + ret = analysisResults.get(StateTransitionCounter); + if(ret == null || (int)ret != 0) { + return false; // no state transitions allowed in a binding reaction + } + ret = analysisResults.get(NumReactants); + if(ret == null || (int)ret != 2) { + return false; // exactly 2 reactants + } + ret = analysisResults.get(NumProducts); + if(ret == null || (int)ret != 1) { + return false; // exactly 1 product + } + List rpList = reactionRule.getReactantPatterns(); // we already know that these 2 lists are not empty + List ppList = reactionRule.getProductPatterns(); + for(ReactantPattern p : rpList) { + if(p.getSpeciesPattern().getMolecularTypePatterns().size() != 1) { // each reactant must have 1 molecule + return false; + } + } + if(ppList.get(0).getSpeciesPattern().getMolecularTypePatterns().size() != 2) { // the product must have 2 molecules + return false; + } + ret = analysisResults.get(BondTransitionCounter); + if(ret == null) { + return false; + } + int bindingTransitions = (int)ret; + if(bindingTransitions == 2) { + return true; // one binding reaction produces 2 binding transitions + } + return false; +} + +public void writeData(StringBuilder sb, Subtype subtype) { // SpringSaLaD exporting the binding rule information + Map analysisResults = new LinkedHashMap<> (); + analizeReaction(analysisResults); + + switch(getSubtype(analysisResults)) { + case CREATION: + case DECAY: + // we invoke the static method below (writeDecayData()) on all species at once from SpringSaLaDExporter.getDocumentAsString() + // because we must collect first all the species that are being created / destroyed and their rates + break; + case TRANSITION: + writeTransitionData(sb, subtype, analysisResults); + break; + case ALLOSTERIC: + writeAllostericData(sb, subtype, analysisResults); + break; + case BINDING: + writeBindingData(sb, subtype, analysisResults); + break; + default: + break; + } +} +public static void writeDecayData(StringBuilder sb, Map> moleculeCreationDecayRates) { + for (Map.Entry> entry : moleculeCreationDecayRates.entrySet()) { + SpeciesContext sc = entry.getKey(); + Pair pair = entry.getValue(); + sb.append("'").append(sc.getName()).append("' : ") + .append("kcreate ").append(pair.one).append(" ") + .append("kdecay ").append(pair.two); + sb.append("\n"); + } +} +private void writeTransitionData(StringBuilder sb, Subtype subtype, Map analysisResults) { + if(subtype != ReactionRuleSpec.Subtype.TRANSITION) { + return; + } + // one reactant and one product, these are the mtp and mcp of the site that is transitioning its state + MolecularTypePattern mtpTransitionReactant = (MolecularTypePattern)analysisResults.get(MtpReactantState + "1"); // one mtp, one mcp + MolecularComponentPattern mcpTransitionReactant = (MolecularComponentPattern)analysisResults.get(McpReactantState + "1"); + MolecularComponentPattern mcpTransitionProduct = (MolecularComponentPattern)analysisResults.get(McpProductState + "1"); + ComponentStatePattern cspTransitionReactant = mcpTransitionReactant.getComponentStatePattern(); + ComponentStatePattern cspTransitionProduct = mcpTransitionProduct.getComponentStatePattern(); + + MolecularTypePattern mtpConditionReactant = null; // the bound condition molecule + MolecularComponentPattern mcpConditionReactant = null; // the bound condition site (must be bond type "Exists") + String stateConditionReactant = ANY_STATE; + + // we may have 2 mtp if the reaction condition is "Bound"! + List rpList = reactionRule.getReactantPatterns(); // we already know that this list is not empty + List mtpReactantList = rpList.get(0).getSpeciesPattern().getMolecularTypePatterns(); + TransitionCondition transitionCondition = TransitionCondition.FREE; // molecules with just one site belong to free + if(mtpReactantList.size() == 1) { // transition reaction condition "None" or "Free" + for(MolecularComponentPattern mcpOtherReactant : mtpTransitionReactant.getComponentPatternList()) { + if(mcpOtherReactant != mcpTransitionReactant) { + if(BondType.None == mcpOtherReactant.getBondType()) { + // transition "None" has the bond types of all sites set to "None" (except the transitioning site which is "Possible") + transitionCondition = TransitionCondition.NONE; + break; + } + } + } + } else if(mtpReactantList.size() == 2) { // transition reaction condition "Bound" + transitionCondition = TransitionCondition.BOUND; + if(mtpTransitionReactant == mtpReactantList.get(0)) { + mtpConditionReactant = mtpReactantList.get(1); + } else { + mtpConditionReactant = mtpReactantList.get(0); + } + // now let's find the Site and the State of the binding condition molecule + for(MolecularComponentPattern mcpCandidate : mtpConditionReactant.getComponentPatternList()) { + if(BondType.Exists == mcpCandidate.getBondType()) { + mcpConditionReactant = mcpCandidate; // found the bond condition site, it's the one with bond type "Exists" + if(!mcpConditionReactant.getComponentStatePattern().isAny()) { + stateConditionReactant = mcpConditionReactant.getComponentStatePattern().getComponentStateDefinition().getName(); + } + break; + } + } + } else { + throw new RuntimeException("writeTransitionData() error: something is wrong"); + } + + RbmKineticLaw kineticLaw = reactionRule.getKineticLaw(); + Expression kon = kineticLaw.getLocalParameterValue(RbmKineticLaw.RbmKineticLawParameterType.MassActionForwardRate); + + sb.append("'").append(reactionRule.getName()).append("' :: "); + sb.append("'").append(mtpTransitionReactant.getMolecularType().getName()).append("' : '") + .append(mcpTransitionReactant.getMolecularComponent().getName()).append("' : '") + .append(cspTransitionReactant.getComponentStateDefinition().getName()); + sb.append("' --> '"); + sb.append(cspTransitionProduct.getComponentStateDefinition().getName()); + sb.append("' Rate ").append(kon.infix()); + sb.append(" Condition ").append(transitionCondition.lngvName); + if(TransitionCondition.BOUND == transitionCondition) { + sb.append(" '").append(mtpConditionReactant.getMolecularType().getName()).append("' : '") + .append(mcpConditionReactant.getMolecularComponent().getName()).append("' : '") + .append(stateConditionReactant).append("'"); + } + sb.append("\n"); +} +private void writeAllostericData(StringBuilder sb, Subtype subtype, Map analysisResults) { + if(subtype != ReactionRuleSpec.Subtype.ALLOSTERIC) { + return; + } + // one reactant and one product, one mtp, the mcp that is changing state + MolecularTypePattern mtpTransitionReactant = (MolecularTypePattern)analysisResults.get(MtpReactantState + "1"); + MolecularComponentPattern mcpTransitionReactant = (MolecularComponentPattern)analysisResults.get(McpReactantState + "1"); + MolecularComponentPattern mcpTransitionProduct = (MolecularComponentPattern)analysisResults.get(McpProductState + "1"); + ComponentStatePattern cspTransitionReactant = mcpTransitionReactant.getComponentStatePattern(); + ComponentStatePattern cspTransitionProduct = mcpTransitionProduct.getComponentStatePattern(); + int conditionIndex = -1; // allosteric site index + MolecularComponentPattern mcpAllostericReactant = null; + for(MolecularComponentPattern mcp : mtpTransitionReactant.getComponentPatternList()) { + conditionIndex++; + if(mcpTransitionReactant == mcp) { + continue; // found the allosteric site index + } + if(mcp.getComponentStatePattern().isAny()) { + continue; // the allosteric state must be explicit + } + mcpAllostericReactant = mcp; + break; // we found the one and only site with an explicit state (other than the state transitioning site + } + if(mcpAllostericReactant == null) { + throw new RuntimeException("writeAllostericData() error: something is wrong"); + } + ComponentStatePattern cspAllostericReactant = mcpAllostericReactant.getComponentStatePattern(); + + RbmKineticLaw kineticLaw = reactionRule.getKineticLaw(); + Expression kon = kineticLaw.getLocalParameterValue(RbmKineticLaw.RbmKineticLawParameterType.MassActionForwardRate); + int transitionIndex = mtpTransitionReactant.getMolecularType().getComponentList().indexOf(mcpTransitionReactant.getMolecularComponent()); + + sb.append("'").append(reactionRule.getName()).append("' :: "); + sb.append("'").append(mtpTransitionReactant.getMolecularType().getName()).append("' : ") + .append("Site ").append(transitionIndex).append(" : '") + .append(cspTransitionReactant.getComponentStateDefinition().getName()); + sb.append("' --> '"); + sb.append(cspTransitionProduct.getComponentStateDefinition().getName()); + sb.append("' Rate ").append(kon.infix()); + sb.append(" Allosteric_Site ").append(conditionIndex); + sb.append(" State '").append(cspAllostericReactant.getComponentStateDefinition().getName()).append("'"); + sb.append("\n"); +} +private void writeBindingData(StringBuilder sb, Subtype subtype, Map analysisResults) { + if(subtype != ReactionRuleSpec.Subtype.BINDING) { + return; + } + // here we only deal with the 2 reactants + MolecularTypePattern mtpReactantOne = (MolecularTypePattern)analysisResults.get(MtpReactantBond + "1"); // '1' - reactant. left side of the bond + MolecularTypePattern mtpReactantTwo = (MolecularTypePattern)analysisResults.get(MtpReactantBond + "2"); // '2' - reactant, right side of the bond + MolecularComponentPattern mcpReactantOne = (MolecularComponentPattern)analysisResults.get(McpReactantBond + "1"); + MolecularComponentPattern mcpReactantTwo = (MolecularComponentPattern)analysisResults.get(McpReactantBond + "2"); + String stateReactantOne = (String)analysisResults.get(CspReactantBond + "1"); + String stateReactantTwo = (String)analysisResults.get(CspReactantBond + "2"); + if(mtpReactantOne == null || mtpReactantTwo == null || mcpReactantOne == null || mcpReactantTwo == null) { + throw new RuntimeException("writeBindingData() error: something is wrong"); + } + + sb.append("'").append(reactionRule.getName()).append("' "); + sb.append("'").append(mtpReactantOne.getMolecularType().getName()).append("' : '") + .append(mcpReactantOne.getMolecularComponent().getName()).append("' : '") + .append(stateReactantOne); + sb.append("' + '"); + sb.append(mtpReactantTwo.getMolecularType().getName()).append("' : '") + .append(mcpReactantTwo.getMolecularComponent().getName()).append("' : '") + .append(stateReactantTwo); + + RbmKineticLaw kineticLaw = reactionRule.getKineticLaw(); + Expression kon = kineticLaw.getLocalParameterValue(RbmKineticLaw.RbmKineticLawParameterType.MassActionForwardRate); + Expression koff = kineticLaw.getLocalParameterValue(RbmKineticLaw.RbmKineticLawParameterType.MassActionReverseRate); + sb.append("' kon ").append(kon.infix()); + sb.append(" koff ").append(koff.infix()); + sb.append(" Bond_Length ").append(Double.toString(getFieldBondLength())); + sb.append("\n"); +} + +public SpeciesContext getCreatedSpecies(SpeciesContextSpec[] speciesContextSpecs, Map analysisResults) { + if(!isCreationReaction(analysisResults)) { + return null; // also verify we use the reserved name SpeciesContextSpec.SourceMoleculeString + } + List ppList = reactionRule.getProductPatterns(); + if(ppList.size() == 1) { + SpeciesPattern spOurs = ppList.get(0).getSpeciesPattern(); + for(SpeciesContextSpec scs : speciesContextSpecs) { + SpeciesContext scCandidate = scs.getSpeciesContext(); + SpeciesPattern spCandidate = scCandidate.getSpeciesPattern(); + if(spOurs.compareEqual(spCandidate)) { + return scCandidate; + } + } + } + return null; +} +public SpeciesContext getDestroyedSpecies(SpeciesContextSpec[] speciesContextSpecs, Map analysisResults) { + if(!isDecayReaction(analysisResults)) { + return null; + } + List rpList = reactionRule.getReactantPatterns(); + if(rpList.size() == 1) { + SpeciesPattern spOurs = rpList.get(0).getSpeciesPattern(); + MolecularType mtOurs = spOurs.getMolecularTypePatterns().get(0).getMolecularType(); + for(SpeciesContextSpec scs : speciesContextSpecs) { + SpeciesContext scCandidate = scs.getSpeciesContext(); + SpeciesPattern spCandidate = scCandidate.getSpeciesPattern(); + MolecularType mtCandidate = spCandidate.getMolecularTypePatterns().get(0).getMolecularType(); + if(mtOurs == mtCandidate) { + return scCandidate; + } + } + } + return null; +} /** * Insert the method's description here. * Creation date: (11/1/2005 10:06:04 AM) * @param issueList java.util.Vector */ +private final static String GenericTip = "Please edit the reaction so that it matches a SpringSaLaD subtype or disable it in this table"; +private final static String SpringSaLaDMsgAtLeastOne = "At least one reactant and one product are required."; +private final static String SpringSaLaDMsgReactionsCannotBe = "SpringSaLaD reactions cannot be located on the Membrane."; +private final static String SpringSaLaDMsgEachReactionMust = "SpringSaLaD requires that each reaction and all its participants must be in the same Structure"; +private final static String SpringSaLaDMsgAnchorCannotBePart = "The reserved site 'Anchor' cannot be part of a non-membrane reactant pattern."; +private final static String SpringSaLaDMsgAnchorCannotBeTarget = "The reserved site 'Anchor' cannot be the target of any SpringSaLaD reaction."; +private final static String SpringSaLaDMsgOnlyAcceptsOneProduct = "SpringSaLaD only accepts one Product."; +private final static String SpringSaLaDMsgOnlyAcceptsTwo = "SpringSaLaD only accepts 2 Reactants for the binding reactions and 1 Reactant for the other subtypes."; +private final static String SpringSaLaDMsgOnlyAcceptsOneBinding = "SpringSaLaD only accepts one binding transition in any reaction."; +private final static String SpringSaLaDMsgOnlyAcceptsOneTransition = "SpringSaLaD only accepts one state transition in any reaction."; +private final static String SpringSaLaDMsgDoesNotAcceptCombination = "SpringSaLaD does not accept a combination of state and binding transitions in any reaction."; +private final static String SpringSaLaDMsgIncompatibleWithAny = "Reaction incompatible with any SpringSaLaD reaction subtype."; +private final static String SpringSaLaDMsgSubtypeMustBeIrreversible = "This reaction subtype must be irreversible. Make the reaction ireversible."; +private final static String SpringSaLaDMsgTransmembraneBinding = "Transmembrane binding not supported"; +private final static String SpringSaLaDMsgSubtypeMustBeReversible = "This reaction subtype must be reversible. Make the reaction reversible and keep Kr at 0."; + public void gatherIssues(IssueContext issueContext, List issueList, ReactionContext rc) { -// ReactionRuleCombo r = new ReactionRuleCombo(this, rc); -// ReactionRule reactionRule = getReactionRule(); + ReactionRuleCombo r = new ReactionRuleCombo(this, rc); + ReactionRule reactionRule = getReactionRule(); // if(!isExcluded() && rc.getSimulationContext().isStoch() && (rc.getSimulationContext().getGeometry().getDimension()>0)) { // boolean haveParticle = false; // boolean haveContinuous = false; @@ -156,6 +885,277 @@ public void gatherIssues(IssueContext issueContext, List issueList, React // issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.SEVERITY_WARNING)); // } // } + if(rc.getSimulationContext().getApplicationType() == SimulationContext.Application.SPRINGSALAD) { + + List rpList = reactionRule.getReactantPatterns(); + List ppList = reactionRule.getProductPatterns(); + if(rpList == null || ppList == null) { + String msg = SpringSaLaDMsgAtLeastOne; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR)); + return; + } + int numReactants = rpList.size(); + int numProducts = ppList.size(); + if(numReactants == 0 || numProducts == 0) { + String msg = SpringSaLaDMsgAtLeastOne; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + + // no reaction can be on the membrane + String reactionStructure = reactionRule.getStructure().getName(); + if(SpringStructureEnum.Membrane.columnName.equals(reactionStructure)) { + String msg = SpringSaLaDMsgReactionsCannotBe; + String tip = msg; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR)); + return; + } + + // the reaction and its participants must all be in the same Structure. + // TODO: this has to be refined, actually reactants bound to a membrane may have sites in the right compartment + // Also, some conditional transition or allosteric reaction may have their condition molecule located in a different + // compartment (can they? TODO check with springsalad !!!) +// List reactionRuleParticipants = reactionRule.getReactionRuleParticipants(); +// for(ReactionRuleParticipant rrp : reactionRuleParticipants) { +// if(!reactionStructure.equals(rrp.getStructure().getName())) { +// String msg = SpringSaLaDMsgEachReactionMust; +// String tip = msg; +// issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR)); +// return; +// } +// } + + // the reserved site 'Anchor' must be not present in any reactant pattern that is not situated on a membrane + // note that we do a similar check for seed species, but this is also needed + // (we can have a bad reactant pattern even though the seed species may be missing) + // TODO:we'll change the Anchor paradigm but this check should still be present some way or another +// for(ReactantPattern rp : rpList) { +// if(!SpringStructureEnum.Membrane.columnName.equals(rp.getStructure().getName())) { +// SpeciesPattern spReactant = rp.getSpeciesPattern(); +// List mtpReactantList = spReactant.getMolecularTypePatterns(); +// for(MolecularTypePattern mtpReactant : mtpReactantList) { +// List mcpReactantList = mtpReactant.getComponentPatternList(); +// for(MolecularComponentPattern mcpReactant : mcpReactantList) { +// MolecularComponent mcReactant = mcpReactant.getMolecularComponent(); +// if(SpeciesContextSpec.AnchorSiteString.equals(mcReactant.getName())) { +// String msg = SpringSaLaDMsgAnchorCannotBePart; +// String tip = msg; +// issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR)); +// return; +// } +// } +// } +// } +// } + + // the reserved site 'Anchor' must not be part of any reaction + // TODO: deal with this once the Anchor is being refactored +// for(ReactantPattern rp : rpList) { +// if(!SpringStructureEnum.Membrane.columnName.equals(rp.getStructure().getName())) { +// continue; // we don't have anchors if it's not a membrane molecule +// } +// SpeciesPattern spReactant = rp.getSpeciesPattern(); +// List mtpReactantList = spReactant.getMolecularTypePatterns(); +// for(MolecularTypePattern mtpReactant : mtpReactantList) { +// List mcpReactantList = mtpReactant.getComponentPatternList(); +// for(MolecularComponentPattern mcpReactant : mcpReactantList) { +// MolecularComponent mcReactant = mcpReactant.getMolecularComponent(); +// if(!SpeciesContextSpec.AnchorSiteString.equals(mcReactant.getName())) { +// continue; +// } +// if(BondType.Possible != mcpReactant.getBondType() || !mcpReactant.getComponentStatePattern().isAny()) { +// String msg = SpringSaLaDMsgAnchorCannotBeTarget; +// String tip = msg; +// issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR)); +// return; +// } +// } +// } +// } + + // ------------------------------------------------------------------------------------------------------------------------ + + Map analysisResults = new LinkedHashMap<> (); + analizeReaction(analysisResults); + Subtype subtype = getSubtype(analysisResults); + if(Subtype.INCOMPATIBLE == subtype) { + if(numProducts > 1) { + String msg = SpringSaLaDMsgOnlyAcceptsOneProduct; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + if(numReactants > 2) { + String msg = SpringSaLaDMsgOnlyAcceptsTwo; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + if(Subtype.INCOMPATIBLE == subtype) { + Object bondObject = analysisResults.get(BondTransitionCounter); + Object stateObject = analysisResults.get(StateTransitionCounter); + if(bondObject != null && stateObject != null) { + int bondTransitionCounter = (int)bondObject; + int stateTransitionCounter = (int)stateObject; + if(bondTransitionCounter > 3) { // a binding reaction produces 2 transitions + String msg = SpringSaLaDMsgOnlyAcceptsOneBinding; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + if(stateTransitionCounter > 1) { + String msg = SpringSaLaDMsgOnlyAcceptsOneTransition; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + if(stateTransitionCounter > 0 && bondTransitionCounter > 0) { + String msg = SpringSaLaDMsgDoesNotAcceptCombination; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + } + if(Subtype.INCOMPATIBLE == subtype) { + String msg = SpringSaLaDMsgIncompatibleWithAny; + String tip = GenericTip; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + } + + // these reactions cannot be reversible + switch(subtype) { + case CREATION: + case DECAY: + case TRANSITION: + case ALLOSTERIC: + if(reactionRule.isReversible()) { + String msg = SpringSaLaDMsgSubtypeMustBeIrreversible; + String tip = "Make this reaction irreversible or disable it in this table."; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + break; + case BINDING: + // we cannot have trans-membrane bonds - and there is no cross-membrane transport support in springsalad + // we make sure the 2 sites bonding are in the same structure, we raise issue if not + MolecularTypePattern mtpOursOne = (MolecularTypePattern)analysisResults.get(MtpReactantBond + "1"); + MolecularComponentPattern mcpOursOne = (MolecularComponentPattern)analysisResults.get(McpReactantBond + "1"); + MolecularTypePattern mtpOursTwo = (MolecularTypePattern)analysisResults.get(MtpReactantBond + "2"); + MolecularComponentPattern mcpOursTwo = (MolecularComponentPattern)analysisResults.get(McpReactantBond + "2"); + MolecularType mtOursOne = mtpOursOne.getMolecularType(); + MolecularType mtOursTwo = mtpOursTwo.getMolecularType(); + MolecularComponent mcOursOne = mcpOursOne.getMolecularComponent(); + MolecularComponent mcOursTwo = mcpOursTwo.getMolecularComponent(); + + Map siteAttributesMapOne = null; + Map siteAttributesMapTwo = null; + SpeciesContextSpec[] speciesContextSpecs = rc.getSpeciesContextSpecs(); + for(SpeciesContextSpec scs : speciesContextSpecs) { + SpeciesContext scCandidate = scs.getSpeciesContext(); + SpeciesPattern spCandidate = scCandidate.getSpeciesPattern(); + MolecularTypePattern mtpCandidate = spCandidate.getMolecularTypePatterns().get(0); + MolecularType mtCandidate = mtpCandidate.getMolecularType(); + if(mtOursOne == mtCandidate) { + siteAttributesMapOne = scs.getSiteAttributesMap(); + } else if(mtOursTwo == mtCandidate) { + siteAttributesMapTwo = scs.getSiteAttributesMap(); + } + } + SiteAttributesSpec sasOne = null; + SiteAttributesSpec sasTwo = null; + for(Map.Entry entry : siteAttributesMapOne.entrySet()) { + MolecularComponentPattern mcpCandidate = entry.getKey(); + if(MolecularComponentPattern.BondType.None != mcpCandidate.getBondType()) { + continue; + } + MolecularComponent mcCandidate = mcpCandidate.getMolecularComponent(); + if(mcOursOne == mcCandidate) { + sasOne = entry.getValue(); + } else if(mcOursTwo == mcCandidate) { + sasTwo = entry.getValue(); + } + } + if(sasOne != null && sasTwo != null) { + for(Map.Entry entry : siteAttributesMapTwo.entrySet()) { + MolecularComponentPattern mcpCandidate = entry.getKey(); + if(MolecularComponentPattern.BondType.None != mcpCandidate.getBondType()) { + continue; + } + MolecularComponent mcCandidate = mcpCandidate.getMolecularComponent(); + if(mcOursOne == mcCandidate) { + sasOne = entry.getValue(); + } else if(mcOursTwo == mcCandidate) { + sasTwo = entry.getValue(); + } + } + if(sasOne.getLocation() != sasTwo.getLocation()) { + String msg = SpringSaLaDMsgTransmembraneBinding; + String tip = "Both binding reactant Sites need to be in the same compartment."; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.ERROR)); + return; + } + } + // binding reactions must be reversible + if(!reactionRule.isReversible()) { + String msg = SpringSaLaDMsgSubtypeMustBeReversible; + String tip = "Make this reaction reversible or disable it in this table."; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + break; + case INCOMPATIBLE: + default: + break; + } + + } else { // not SpringSaLaD + if(reactionRule.getReactantPatterns() == null || reactionRule.getProductPatterns() == null) { + return; // issue reported in physiology + } + // check if there are disjoint patterns (molecules not explicitely connected through components) + for(ReactantPattern rp : reactionRule.getReactantPatterns()) { + SpeciesPattern sp = rp.getSpeciesPattern(); + if(sp.getMolecularTypePatterns().size() < 2) { + continue; // no point in looking since the pattern has a single molecule + } + boolean isDisjoint = false; + for(MolecularTypePattern mtp : sp.getMolecularTypePatterns()) { + if(mtp.isDisjoint()) { + isDisjoint = true; + break; // found an unconnected molecule in the species pattern + } + } + if(isDisjoint) { // tell the world + String message = "Found disjoint sets in a reactant pattern, as in A().B() with no explicit bond through components."; + String toolTip = "These type of patterns may make NFSim run slower or even fail. Is it highly advised to express this pattern with a different syntax."; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, message, toolTip, Issue.Severity.WARNING)); + return; + } + } + for(ProductPattern pp : reactionRule.getProductPatterns()) { + SpeciesPattern sp = pp.getSpeciesPattern(); + if(sp.getMolecularTypePatterns().size() < 2) { + continue; + } + boolean isDisjoint = false; + for(MolecularTypePattern mtp : sp.getMolecularTypePatterns()) { + if(mtp.isDisjoint()) { + isDisjoint = true; + break; + } + } + if(isDisjoint) { + String message = "Found disjoint sets in a product pattern, as in A().B() with no explicit bond through components."; + String toolTip = "These type of patterns may make NFSim run slower or even fail. Is it highly advised to express this pattern with a different syntax."; + issueList.add(new Issue(r, issueContext, IssueCategory.Identifiers, message, toolTip, Issue.Severity.WARNING)); + return; + } + } + } } /** @@ -251,10 +1251,18 @@ public ReactionRule getModelProcess() { return reactionRule; } - @Override public boolean isFast() { return false; } +public double getFieldBondLength() { + return fieldBondLength; +} + +public void setFieldBondLength(double fieldBondLength) { + this.fieldBondLength = fieldBondLength; +} + + } diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/SimulationContext.java b/vcell-core/src/main/java/cbit/vcell/mapping/SimulationContext.java index aa631eb466..a2d2d79b37 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/SimulationContext.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/SimulationContext.java @@ -29,6 +29,7 @@ import org.vcell.util.Issue.Severity; import org.vcell.util.IssueContext.ContextType; import org.vcell.util.document.BioModelChildSummary.MathType; +import org.vcell.util.exe.ExecutableStatus; import org.vcell.util.document.DocumentValidUtil; import org.vcell.util.document.ExternalDataIdentifier; import org.vcell.util.document.Identifiable; @@ -42,6 +43,7 @@ import cbit.vcell.biomodel.BioModel; import cbit.vcell.bionetgen.BNGOutputSpec; import cbit.vcell.data.DataContext; +import cbit.vcell.export.SpringSaLaDExporter; import cbit.vcell.field.FieldFunctionArguments; import cbit.vcell.field.FieldUtilities; import cbit.vcell.geometry.Geometry; @@ -62,6 +64,7 @@ import cbit.vcell.mapping.MicroscopeMeasurement.GaussianConvolutionKernel; import cbit.vcell.mapping.MicroscopeMeasurement.ProjectionZKernel; import cbit.vcell.mapping.ParameterContext.LocalParameter; +import cbit.vcell.mapping.RulebasedTransformer.ReactionRuleAnalysisReport; import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; import cbit.vcell.mapping.spatial.CurveObject; import cbit.vcell.mapping.spatial.PointObject; @@ -91,6 +94,7 @@ import cbit.vcell.model.ReactionStep; import cbit.vcell.model.SpeciesContext; import cbit.vcell.model.Structure; +import cbit.vcell.model.Structure.SpringStructureEnum; import cbit.vcell.modelopt.AnalysisTask; import cbit.vcell.modelopt.ParameterEstimationTask; import cbit.vcell.parser.AutoCompleteSymbolFilter; @@ -580,7 +584,8 @@ public SimulationContext getSimulationContext(){ public enum Application { NETWORK_DETERMINISTIC(MathType.Deterministic), NETWORK_STOCHASTIC(MathType.Stochastic), - RULE_BASED_STOCHASTIC(MathType.RuleBased); + RULE_BASED_STOCHASTIC(MathType.RuleBased), + SPRINGSALAD(MathType.SpringSaLaD); final public MathType mathType; @@ -1342,6 +1347,24 @@ public void gatherIssues(IssueContext issueContext, List issueVector, boo } } } + if(applicationType.equals(Application.SPRINGSALAD)) { + if(getModel().getStructures().length != 3) { + String msg = "SpringSaLaD requires exactly 3 Structures: one Membrane and two Features (Compartments)"; + issueVector.add(new Issue(getModel().getStructures()[0], issueContext, IssueCategory.Identifiers, msg, Issue.Severity.ERROR)); + } + for(Structure struct : getModel().getStructures()) { + String name = struct.getName(); + if(!Structure.springStructureSet.contains(name)) { + String msg = "'" + name + "' not legal identifier for SpringSaLaD applications."; + if(struct instanceof Feature) { + msg += " Try using '" + SpringStructureEnum.Intracellular.columnName + "' or '" + SpringStructureEnum.Extracellular.columnName + "'."; + } else { // Membrane + msg += " Try using '" + SpringStructureEnum.Membrane.columnName + "'."; + } + issueVector.add(new Issue(struct, issueContext, IssueCategory.Identifiers, msg, Issue.Severity.ERROR)); + } + } + } if (fieldBioEvents!=null){ for (BioEvent bioEvent : fieldBioEvents){ bioEvent.gatherIssues(issueContext, issueVector); @@ -3118,6 +3141,21 @@ public DataContext getDataContext() { */ public SimContextTransformer createNewTransformer(){ switch (applicationType) { + case SPRINGSALAD: + // + // + // TODO: can't use RulebasedTransformer !!! + // because the new LangevinMathMapping(this, callback, networkGenReq); call below will invoke the transformer, + // and the transformer will call generateNetwork(), which will call executeBNG(), with the wrong expectedReturnCodes parameter, + // which will do a setStatus(ExecutableStatus.COMPLETE); right from the start + // + // TODO: can't use null either, we must get specific transformer, that's a simplified RulebasedTransformer + // because in LangevinMathMapping.addStrictMassActionParticleJumpProcess() at + // line 945 we do ReactionRuleAnalysisReport rrarBiomodelForward = ruleBasedTransformation.getRulesForwardMap().get(reactionRule); + // In other words, we need the transformation for the forward map and the reverse map + // + // +// return null; case RULE_BASED_STOCHASTIC: return new RulebasedTransformer(); case NETWORK_DETERMINISTIC: @@ -3232,6 +3270,9 @@ public MathMapping createNewMathMapping(MathMappingCallback callback, NetworkGen case NETWORK_DETERMINISTIC: mostRecentlyCreatedMathMapping = new DiffEquMathMapping(this, callback, networkGenReq); break; + case SPRINGSALAD: + mostRecentlyCreatedMathMapping = new LangevinMathMapping(this, callback, networkGenReq); + break; } VCAssert.assertFalse(mostRecentlyCreatedMathMapping == null, "math mapping not generated" ); diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/SiteAttributesSpec.java b/vcell-core/src/main/java/cbit/vcell/mapping/SiteAttributesSpec.java new file mode 100644 index 0000000000..a0b560f784 --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/mapping/SiteAttributesSpec.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package cbit.vcell.mapping; + +import java.awt.Color; +import java.io.PrintWriter; +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import cbit.vcell.model.*; + +import org.vcell.model.rbm.ComponentStateDefinition; +import org.vcell.model.rbm.ComponentStatePattern; +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularType; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; +import org.vcell.util.*; +import org.vcell.util.Issue.IssueCategory; +import org.vcell.util.Issue.IssueSource; +import org.vcell.util.IssueContext.ContextType; +import org.vcell.util.document.Identifiable; +import org.vcell.util.springsalad.Colors; +import org.vcell.util.springsalad.IOHelp; +import org.vcell.util.springsalad.NamedColor; + +@SuppressWarnings("serial") +public class SiteAttributesSpec implements Serializable, Identifiable, Displayable, IssueSource { + private final SpeciesContextSpec fieldSpeciesContextSpec; + private MolecularComponentPattern fieldMolecularComponentPattern = null; + private double fieldRadius = 1.0; + private double fieldDiffusionRate = 1.0; + private Structure fieldLocation = null; // feature or membrane + private Coordinate fieldCoordinate = new Coordinate(0,0,0); // double x,y,z; has distanceTo() + private NamedColor fieldColor = Colors.RED; + // the ComponentStatePattern must not be Any; can be recovered from the MolecularComponentPattern + // the BondType must be None, can be recovered from the MolecularComponentPattern + + public SiteAttributesSpec(SpeciesContextSpec scs, MolecularComponentPattern mcp, Structure structure) { + fieldSpeciesContextSpec = scs; + setMolecularComponentPattern(mcp); + setLocation(structure); + } + public SiteAttributesSpec(SpeciesContextSpec scs, MolecularComponentPattern mcp, double radius, double diffusion, Structure location, Coordinate coordinate, NamedColor color) { + fieldSpeciesContextSpec = scs; + setMolecularComponentPattern(mcp); + setRadius(radius); + setDiffusionRate(diffusion); + setLocation(location); + setCoordinate(coordinate); + setColor(color); + } + + public SpeciesContextSpec getSpeciesContextSpec() { + return fieldSpeciesContextSpec; + } + public MolecularComponentPattern getMolecularComponentPattern() { + return fieldMolecularComponentPattern; + } + public void setMolecularComponentPattern(MolecularComponentPattern molecularComponentPattern) { + this.fieldMolecularComponentPattern = molecularComponentPattern; + } + + public double getRadius() { + return fieldRadius; + } + public void setRadius(double radius) { + this.fieldRadius = radius; + } + + public double getDiffusionRate() { + return fieldDiffusionRate; + } + public void setDiffusionRate(double diffusionRate) { + this.fieldDiffusionRate = diffusionRate; + } + + public Structure getLocation() { + return fieldLocation; + } + public void setLocation(Structure location) { + this.fieldLocation = location; + } + + public Coordinate getCoordinate() { + return fieldCoordinate; + } + public void setCoordinate(Coordinate coordinate) { + this.fieldCoordinate = coordinate; + } + + public NamedColor getColor() { + return fieldColor; + } + public void setColor(NamedColor color) { + this.fieldColor = color; + } + public double getX() { + return getCoordinate().getX(); + } + public double getY() { + return getCoordinate().getY(); + } + public double getZ() { + return getCoordinate().getZ(); + } + + public int getIndex() { + MolecularComponent mc = getMolecularComponentPattern().getMolecularComponent(); + int index = mc.getIndex(); + return index; + } + + public void writeType(StringBuilder sb) { // I/O the Site Type (mMlecularComponent) + if(getMolecularComponentPattern() == null) { + throw new RuntimeException("writeType(): MolecularComponentPattern is null"); + } + MolecularComponent mc = getMolecularComponentPattern().getMolecularComponent(); + List csdList = mc.getComponentStateDefinitions(); + sb.append("TYPE: Name \"" + mc.getName() + "\""); + sb.append(" Radius " + IOHelp.DF[5].format(getRadius()) + " D " + IOHelp.DF[3].format(getDiffusionRate()) + " Color " + getColor().getName()); + sb.append(" STATES "); + for (ComponentStateDefinition state : csdList) { + sb.append("\"" + state.getName() + "\"" + " "); + } + sb.append("\n"); + } + public void writeSite(StringBuilder sb) { + if(getMolecularComponentPattern() == null) { + throw new RuntimeException("writeSite(): MolecularComponentPattern is null"); + } + ComponentStatePattern csp = getMolecularComponentPattern().getComponentStatePattern(); + if(csp == null) { + sb.append("SITE " + (this.getIndex()-1) + " : " + getLocation().getName() + " : Initial State '" + "ERROR: at least one State is needed" + "'"); + sb.append("\n"); + return; + } + ComponentStateDefinition csd = csp.getComponentStateDefinition(); + if(csd == null) { + throw new RuntimeException("writeSite(): csd is null"); + } + String initialState = csd.getName(); + sb.append("SITE " + (this.getIndex()-1) + " : " + getLocation().getName() + " : Initial State '" + initialState + "'"); + sb.append("\n"); + sb.append(" "); + this.writeType(sb); // ex: TYPE: Name "Type2" Radius 1.00000 D 1.000 Color LIME STATES "State0" "State1" + sb.append(" " + "x " + IOHelp.DF[5].format(getX()) + " y " + IOHelp.DF[5].format(getY()) + + " z " + IOHelp.DF[5].format(getZ()) + " "); // ex: x 4.00000 y 4.00000 z 20.00000 + sb.append("\n"); + } + + + + public void gatherIssues(IssueContext issueContext, List issueVector) { + issueContext = issueContext.newChildContext(ContextType.SiteAttributesSpec, this); + + if(fieldLocation instanceof Membrane) { + String tip = "A generic issue for SiteAttributesSpec entity."; + String msg = "Location is a Membrane."; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + } + } + + public static final String typeName = "SiteAttributesSpec"; + @Override + public String getDisplayName() { + if(fieldMolecularComponentPattern != null) { + return fieldMolecularComponentPattern.getMolecularComponent().getDisplayName(); + } + return("?"); + } + @Override + public String getDisplayType() { + return typeName; + } + +} + \ No newline at end of file diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/SpeciesContextSpec.java b/vcell-core/src/main/java/cbit/vcell/mapping/SpeciesContextSpec.java index b8543c4939..3c696ffbab 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/SpeciesContextSpec.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/SpeciesContextSpec.java @@ -15,22 +15,34 @@ import java.math.BigDecimal; import java.math.MathContext; import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import cbit.vcell.model.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.vcell.model.rbm.ComponentStateDefinition; +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularComponentPattern; +import org.vcell.model.rbm.MolecularType; +import org.vcell.model.rbm.MolecularTypePattern; +import org.vcell.model.rbm.SpeciesPattern; import org.vcell.util.*; import org.vcell.util.Issue.IssueCategory; import org.vcell.util.Issue.IssueSource; import org.vcell.util.IssueContext.ContextType; import org.vcell.util.document.Identifiable; +import cbit.vcell.geometry.Geometry; import cbit.vcell.geometry.GeometryClass; +import cbit.vcell.geometry.GeometrySpec; import cbit.vcell.geometry.SubVolume; import cbit.vcell.geometry.SurfaceClass; import cbit.vcell.geometry.surface.GeometricRegion; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.Kind; import cbit.vcell.mapping.spatial.SpatialObject; import cbit.vcell.mapping.spatial.SpatialObject.QuantityCategory; @@ -47,6 +59,7 @@ import cbit.vcell.parser.SymbolTable; import cbit.vcell.parser.SymbolTableEntry; import cbit.vcell.parser.SymbolTableFunctionEntry; +import cbit.vcell.solver.Simulation; import cbit.vcell.units.VCUnitDefinition; import net.sourceforge.interval.ia_math.RealInterval; @@ -60,6 +73,15 @@ public class SpeciesContextSpec implements Matchable, ScopedSymbolTable, Seriali private static final String PROPERTY_NAME_WELL_MIXED = "wellMixed"; private static final String PROPERTY_NAME_FORCECONTINUOUS = "forceContinuous"; + public static final boolean TrackClusters = true; // SpringSaLaD specific + public static final boolean InitialLocationRandom = true; + private static final String InitialLocationRandomString = "Random"; + private static final String InitialLocationSetString = "Set"; + public static final String SourceMoleculeString = "Source"; // molecule used in creation reaction subtype (reserved name) + public static final String SinkMoleculeString = "Sink"; // molecule used in decay reaction subtype (reserved name) + public static final String AnchorSiteString = "Anchor"; // required name for reserved special Site of membrane species + public static final String AnchorStateString = "Anchor"; // required name for reserved special State of the Anchor site + public class SpeciesContextSpecNameScope extends BioNameScope { private final NameScope children[] = new NameScope[0]; // always empty @@ -406,9 +428,7 @@ public void targetPropertyChange(PropertyChangeEvent evt) { } } } - } - private SpeciesContext speciesContext = null; private static final boolean DEFAULT_CLAMPED = false; @@ -419,6 +439,10 @@ public void targetPropertyChange(PropertyChangeEvent evt) { // private boolean bEnableDiffusing = DEFAULT_ENABLE_DIFFUSING; private Boolean bWellMixed = DEFAULT_WELL_MIXED; private Boolean bForceContinuous = DEFAULT_FORCECONTINUOUS; + + private Set internalLinkSet = new LinkedHashSet<> (); // SpringSaLaD + private Map siteAttributesMap = new LinkedHashMap<> (); + protected transient java.beans.VetoableChangeSupport vetoPropertyChange; private SpeciesContextSpecParameter[] fieldParameters = null; private SpeciesContextSpecProxyParameter[] fieldProxyParameters = new SpeciesContextSpecProxyParameter[0]; @@ -510,7 +534,7 @@ public SpeciesContextSpec(SpeciesContextSpec speciesContextSpec, SimulationConte fieldParameters[i] = new SpeciesContextSpecParameter(otherParm.getName(),otherParmExp,otherParm.getRole(),otherParm.getUnitDefinition(),otherParm.getDescription()); } refreshDependencies(); -} +} public SpeciesContextSpec(SpeciesContext speciesContext, SimulationContext argSimulationContext) { @@ -606,7 +630,7 @@ private VCUnitDefinition getLengthPerTimeUnit() { ModelUnitSystem modelUnitSystem = getSimulationContext().getModel().getUnitSystem(); VCUnitDefinition lengthPerTimeUnit = modelUnitSystem.getLengthUnit().divideBy(modelUnitSystem.getTimeUnit()); return lengthPerTimeUnit; -} +} public void initializeForSpatial() { if(getDiffusionParameter() != null && getDiffusionParameter().getExpression() != null && getDiffusionParameter().getExpression().isZero()) { @@ -631,6 +655,49 @@ public void initializeForSpatial() { } } +public void initializeForSpringSaLaD(MolecularType molecularType) { + if(getSpeciesContext() != null) { + SpeciesPattern sp = getSpeciesContext().getSpeciesPattern(); + if(sp == null) { + return; + } + // in SpringSaLaD all seed species are single molecule, we don't use complexes + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); + MolecularType mt = mtp.getMolecularType(); + if(molecularType != null && molecularType != mt) { + return; // this molecule is unchanged, nothing to do + } + // molecularType == null : full initialization + // molecularType != null and molecularType == mt : this molecule has a different + // number of components or a different component order + List componentList = mt.getComponentList(); + for(MolecularComponent mc : componentList) { + MolecularComponentPattern mcp = mtp.getMolecularComponentPattern(mc); + if(siteAttributesMap.containsKey(mcp)) { + continue; // this is set, nothing to do + } + SiteAttributesSpec sas = siteAttributesMap.get(mcp); + if(sas == null || sas.getMolecularComponentPattern() == null) { + sas = new SiteAttributesSpec(this, mcp, getSpeciesContext().getStructure()); + siteAttributesMap.put(mcp, sas); + } + } + + if(getInternalLinkSet().isEmpty()) { + for(int i=0; i< componentList.size()-1; i++) { + MolecularComponent mcOne = componentList.get(i); + MolecularComponent mcTwo = componentList.get(i+1); + MolecularComponentPattern mcpOne = mtp.getMolecularComponentPattern(mcOne); + MolecularComponentPattern mcpTwo = mtp.getMolecularComponentPattern(mcTwo); + MolecularInternalLinkSpec link = new MolecularInternalLinkSpec(this, mcpOne, mcpTwo); + // TODO: set x,y,z instead, link will be computed +// link.setLinkLength(2.0); + internalLinkSet.add(link); + } + } + } +} + public VCUnitDefinition computeFluxUnit() { return speciesContext.getUnitDefinition().multiplyBy(getLengthPerTimeUnit()); } @@ -888,6 +955,173 @@ else if(!candidate.isForceContinuous() && !candidate.isConstant()) { } } } + + if(getSimulationContext().getApplicationType() == SimulationContext.Application.SPRINGSALAD) { + SpeciesContext sc = getSpeciesContext(); + if(sc != null && sc.getSpeciesPattern() != null) { + SpeciesPattern sp = sc.getSpeciesPattern(); + List mtpList = sp.getMolecularTypePatterns(); + if(mtpList.size() != 1) { + String msg = "SpringSaLaD requires all Species to be associated with exactly one MolecularType."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + MolecularTypePattern mtp = mtpList.get(0); + List mcpList = mtp.getComponentPatternList(); + if(SourceMoleculeString.equals(sc.getName()) || SinkMoleculeString.equals(sc.getName())) { + if(mcpList.size() != 0) { + String msg = "SpringSaLaD reserved Molecules '" + SourceMoleculeString + "' and '" + SinkMoleculeString + "' must not have any sites defined"; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + if(mcpList.size() == 0) { + String msg = "SpringSaLaD requires the MolecularType to have at least one Site."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + for(MolecularComponentPattern mcp : mcpList) { + MolecularComponent mc = mcp.getMolecularComponent(); + List csd = mc.getComponentStateDefinitions(); + if(csd.size() < 1) { + String msg = "Each Site must have at least one State."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + for(MolecularInternalLinkSpec mils : getInternalLinkSet()) { + if(mils.getMolecularComponentPatternOne() == mils.getMolecularComponentPatternTwo()) { + String msg = "Both sites of the Link are identical."; + String tip = "A site cannot be linked to itself."; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + if(mcpList.size()>1 && mcpList.size() > getInternalLinkSet().size()+1) { + String msg = "Link chain within the molecule has at least one discontinuity."; + String tip = "One or more links are missing"; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + for(MolecularInternalLinkSpec candidate : getInternalLinkSet()) { + for(MolecularInternalLinkSpec other : getInternalLinkSet()) { + if(candidate == other) { + continue; + } + if(candidate.compareEqual(other)) { + String one = candidate.getMolecularComponentPatternOne().getMolecularComponent().getName(); + String two = candidate.getMolecularComponentPatternTwo().getMolecularComponent().getName(); + String msg = "Duplicate link: " + one + " :: " + two; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + } + if(mcpList.size() == 1) { + String msg = "Internal Links are possible only when the Molecule has at least 2 sites."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + } + + // if the species context is on membrane it must have a site named Anchor on the membrane, the other sites must NOT be on membrane + Structure struct = sc.getStructure(); + MolecularComponentPattern mcpAnchor = null; + if(struct instanceof Membrane) { + boolean anchorExists = false; + for(MolecularComponentPattern mcp : mcpList) { + MolecularComponent mc = mcp.getMolecularComponent(); + if(AnchorSiteString.equals(mc.getName())) { + anchorExists = true; + mcpAnchor = mcp; + SiteAttributesSpec sas = getSiteAttributesMap().get(mcp); + if(!(sas.getLocation() instanceof Membrane)) { + String msg = "The reserved Site 'Anchor' must be located on a Membrane."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } else { // all the other sites of a membrane species must not be on the membrane + SiteAttributesSpec sas = getSiteAttributesMap().get(mcp); + if(sas.getLocation() instanceof Membrane) { + String msg = "All the Sites of a Membrane species, other than the 'Anchor', must NOT be located on a Membrane."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + } + if(anchorExists == false) { + String msg = "Species localized on a membrane require a reserved site named 'Anchor'"; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } else { + // the anchor Site must have just one single State, also named Anchor + String msg = "The reserved Site 'Anchor' must have exactly one State named 'Anchor'."; + MolecularComponent mcAnchor = mcpAnchor.getMolecularComponent(); + List csdAnchorList = mcAnchor.getComponentStateDefinitions(); + if(csdAnchorList.size() != 1) { + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + ComponentStateDefinition csdAnchor = csdAnchorList.get(0); + if(!AnchorStateString.equals(csdAnchor.getName())) { + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + } else { // a species inside a Feature must NOT have a site named Anchor + for(MolecularComponentPattern mcp : mcpList) { + MolecularComponent mc = mcp.getMolecularComponent(); + if(AnchorSiteString.equals(mc.getName())) { + String msg = "The reserved Site 'Anchor' can be used only for a Species located on a Membrane."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + } + + // each species context is associated with one and only one molecular type and viceversa (biunivocal correspondence) + SpeciesContextSpec[] scsArray = getSimulationContext().getReactionContext().getSpeciesContextSpecs(); + for(SpeciesContextSpec scsCandidate : scsArray) { + SpeciesContext scCandidate = scsCandidate.getSpeciesContext(); + if(sc == scCandidate) { + continue; // skip self + } + if(scCandidate.getSpeciesPattern() == null) { + // invalid, we'll deal with it separately - skip for now since will generate a NPE + continue; + } + MolecularType mtCandidate = scCandidate.getSpeciesPattern().getMolecularTypePatterns().get(0).getMolecularType(); + if(mtp.getMolecularType() == mtCandidate) { + String msg = "There must be a biunivocal correspondence between the Species and the associated MolecularType."; + String tip = "In SpringSaLaD, two Seed Species cannot share the same Molecular Type."; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } + + // the species context and the molecular type must have the same name + if(!sc.getName().equals(mtp.getMolecularType().getName())) { + String msg = "The Species and the Molecular Type must share the same name."; + String tip = msg; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + return; + } + } else { + String msg = "SpringSaLaD requires all Species to be associated with a MolecularType."; + String tip = "Associate a MolecularType to the Species in Physiology / Species panel."; + issueVector.add(new Issue(this, issueContext, IssueCategory.Identifiers, msg, tip, Issue.Severity.WARNING)); + } + } } /** @@ -1791,6 +2025,21 @@ public List computeApplicableParameterList() { return speciesContextSpecParameterList; } +public Set getInternalLinkSet() { + return internalLinkSet; +} +public void setInternalLinkSet(Set internalLinkSet) { + this.internalLinkSet = internalLinkSet; +} + + +public Map getSiteAttributesMap() { + return siteAttributesMap; +} +public void setSiteAttributesMap(Map siteAttributesMap) { + this.siteAttributesMap = siteAttributesMap; +} + public SpatialQuantity[] getVelocityQuantities(QuantityComponent component) { ArrayList velocityQuantities = new ArrayList(); for (SpatialObject spatialObject : simulationContext.getSpatialObjects(speciesContext.getStructure())){ @@ -1803,12 +2052,96 @@ public SpatialQuantity[] getVelocityQuantities(QuantityComponent component) { return velocityQuantities.toArray(new SpatialQuantity[0]); } - @Override public Kind getSimulationContextKind() { return SimulationContext.Kind.SPECIFICATIONS_KIND; } +public void writeData(StringBuilder sb) { // SpringSaLaD exporting the species / molecule information + if(getSimulationContext().getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + // ---------------------------------------------------------------------------------- +// private Set internalLinkSet = new LinkedHashSet<> (); // SpringSaLaD +// private Map siteAttributesMap = new LinkedHashMap<> (); +// for(Map.Entry entry : getSiteAttributesMap().entrySet()) { +// MolecularComponentPattern key = entry.getKey(); +// SiteAttributesSpec sas = entry.getValue(); + + Geometry geometry = getSimulationContext().getGeometry(); + GeometryContext geometryContext = getSimulationContext().getGeometryContext(); + GeometrySpec geometrySpec = geometry.getGeometrySpec(); + ReactionContext reactionContext = getSimulationContext().getReactionContext(); + SpeciesContextSpec[] speciesContextSpecs = reactionContext.getSpeciesContextSpecs(); + ReactionSpec[] reactionSpecs = reactionContext.getReactionSpecs(); + ReactionRuleSpec[] reactionRuleSpecs = reactionContext.getReactionRuleSpecs(); + String name = getSpeciesContext().getName(); + if(SourceMoleculeString.equals(name) || SinkMoleculeString.equals(name)) { + return; // the solver doesn't need to know about Sink and Source + } + SpeciesContextSpecParameter initialCountParameter = getInitialCountParameter(); + SpeciesPattern sp = getSpeciesContext().getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns() == null || sp.getMolecularTypePatterns().isEmpty()) { + sb.append("MOLECULE: \"" + getSpeciesContext().getName() + "\" " + "ERROR"); + sb.append("\n"); + sb.append("\n"); + return; + } + MolecularType mt = sp.getMolecularTypePatterns().get(0).getMolecularType(); + List componentList = mt.getComponentList(); + int dimension = geometry.getDimension(); + + sb.append("MOLECULE: \"" + getSpeciesContext().getName() + "\" " + getSpeciesContext().getStructure().getName() + + " Number " + initialCountParameter.getExpression().infix() + + " Site_Types " + componentList.size() + " Total" + "_Sites " + siteAttributesMap.size() + + " Total_Links " + internalLinkSet.size() + " is2D " + (dimension == 2 ? true : false)); // TODO: molecule is flat, unrelated to geometry + sb.append("\n"); + sb.append("{"); + sb.append("\n"); + + for(Map.Entry entry : siteAttributesMap.entrySet()) { + SiteAttributesSpec sas = entry.getValue(); + sb.append(" "); + sas.writeType(sb); // writeMolecularComponent + } + sb.append("\n"); + for(Map.Entry entry : siteAttributesMap.entrySet()) { + SiteAttributesSpec sas = entry.getValue(); + sb.append(" "); + sas.writeSite(sb); + } + sb.append("\n"); + for(MolecularInternalLinkSpec mils : internalLinkSet) { + sb.append(" "); + mils.writeLink(sb); + } + + sb.append("\n"); + sb.append(" Initial_Positions: "); + if(InitialLocationRandom) { + sb.append(InitialLocationRandomString); + sb.append("\n"); +// } else { +// sb.append(InitialLocationSetString); +// sb.append("\n"); +// sb.append(" x: " + IOHelp.printArrayList(initialCondition.getXIC(), 5)); +// sb.append("\n"); +// sb.append(" y: " + IOHelp.printArrayList(initialCondition.getYIC(), 5)); +// sb.append("\n"); +// sb.append(" z: " + IOHelp.printArrayList(initialCondition.getZIC(), 5)); +// sb.append("\n"); + } + sb.append("}"); + sb.append("\n"); + sb.append("\n"); + return; +} +public String getFilename() { // SpringSaLaD specific, external file with molecule information + return null; // not implemented +} + + public static final String typeName = "SpeciesContextSpec"; @Override public String getDisplayName() { @@ -1822,4 +2155,5 @@ public String getDisplayType() { return typeName; } + } diff --git a/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleJumpProcess.java b/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleJumpProcess.java new file mode 100644 index 0000000000..e66d1fe878 --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleJumpProcess.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package cbit.vcell.math; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.vcell.util.CommentStringTokenizer; +import org.vcell.util.Compare; +import org.vcell.util.Matchable; + +import cbit.vcell.mapping.ReactionRuleSpec; +import cbit.vcell.mapping.ReactionRuleSpec.Subtype; +import cbit.vcell.mapping.ReactionRuleSpec.TransitionCondition; +import cbit.vcell.parser.Expression; +import cbit.vcell.parser.ExpressionBindingException; +import cbit.vcell.parser.ExpressionException; + +public class LangevinParticleJumpProcess extends ParticleJumpProcess { + private ReactionRuleSpec rrr = null; + private Subtype subtype = null; + private TransitionCondition transitionCondition = null; + private double bondLength = 0; + + + +/** + * JumpProcess constructor comment. + * @param var cbit.vcell.math.Variable + * @param initialExp cbit.vcell.parser.Expression + * @param rateExp cbit.vcell.parser.Expression + */ +public LangevinParticleJumpProcess(String name, List particles, JumpProcessRateDefinition rateDefinition, List actions, ProcessSymmetryFactor processSymmetryFactor) +{ + super(name, particles, rateDefinition, actions, processSymmetryFactor); +} + + + +/** + * Compare two Jump processes. + * @return boolean + * @param object java.lang.Object + */ +public boolean compareEqual(org.vcell.util.Matchable object) +{ + if (!(object instanceof LangevinParticleJumpProcess)) { + return false; + } + if(false) { // TODO: compare everything that needs comparing + return false; + } + return super.compareEqual(object); +} + + +/* + * Write the instance of the class to VCML. + * @return java.lang.String + */ +public String getVCML() +{ + StringBuffer buffer = new StringBuffer(); + // the jump process will be written inside compartment brackets, therefore a "\t" is needed + buffer.append("\t"+VCML.ParticleJumpProcess+"\t"+getName()+" "+VCML.BeginBlock+"\n"); + buffer.append("\t\t" + VCML.Subtype + "\t\t\t" + subtype.columnName+"\n"); + if(Subtype.TRANSITION == subtype) { + buffer.append("\t\t" + VCML.TransitionCondition + "\t\t" + transitionCondition.vcellName + "\n"); + } else { + buffer.append("\t\t" + VCML.TransitionCondition + "\t\t" + " - " + "\n"); + } + if(Subtype.BINDING == subtype) { + buffer.append("\t\t" + VCML.BondLength + "\t\t\t" + bondLength + "\n"); + } else { + buffer.append("\t\t" + VCML.BondLength + "\t\t\t" + " - " + "\n"); + } + for (ParticleVariable particleVar : particles){ + buffer.append("\t\t"+VCML.SelectedParticle+"\t\t"+particleVar.getName()+"\n"); + } + buffer.append("\t\t"+getParticleRateDefinition().getVCML()+";\n"); + for(Action action : actions){ + buffer.append(action.getVCML()); + } + if (processSymmetryFactor!=null){ + buffer.append("\t\t"+VCML.ProcessSymmetryFactor+"\t\t"+this.processSymmetryFactor.getFactor()+"\n"); + } + buffer.append("\t"+" "+VCML.EndBlock+"\n"); +// if (this.processParticleMappings != null){ +// buffer.append("\t"+" "+VCML.ProcessParticleMappings+"{\n"); +// for(ProcessParticleMapping mapping : this.processParticleMappings){ +// buffer.append(mapping.getVCML()); +// } +// buffer.append("\t"+" "+VCML.EndBlock+"\n"); +// } + return buffer.toString(); +} + + +/** + * This method was created by a SmartGuide. + * @param tokens java.util.StringTokenizer + * @exception java.lang.Exception The exception description. + */ +public static LangevinParticleJumpProcess fromVCML(MathDescription mathDesc, CommentStringTokenizer tokens) throws MathException, ExpressionException { + String token = tokens.nextToken(); + String name = token; + token = tokens.nextToken(); + if (!token.equals(VCML.BeginBlock)){ + throw new MathFormatException("expecting "+VCML.BeginBlock+", found "+token); + } + + Subtype subtype = null; + TransitionCondition transitionCondition = null; // may be null if Subtype is not TRANSITION + double bondLength = -1; + ArrayList particles = new ArrayList(); + JumpProcessRateDefinition particleRateDef = null; + ArrayList actions = new ArrayList(); + ProcessSymmetryFactor symmetryFactor = null; + + token = tokens.nextToken(); + while(!token.equals(VCML.EndBlock)) { + if(token.equals(VCML.Subtype)) { + token = tokens.nextToken(); + String subtypeName = token; + subtype = Subtype.valueOf(subtypeName); + if(subtype == null) { + throw new IllegalArgumentException("Invalid Subtype: " + subtypeName); + } + } else if(token.equals(VCML.TransitionCondition)) { + if(Subtype.TRANSITION == subtype) { + token = tokens.nextToken(); + String transitionConditionName = token; + transitionCondition = TransitionCondition.valueOf(transitionConditionName); + if(subtype == null) { + throw new IllegalArgumentException("Invalid TransitionCondition: " + transitionConditionName); + } + } + } else if(token.equals(VCML.BondLength)) { + token = tokens.nextToken(); + try { + bondLength = Double.parseDouble(token); + } catch(NumberFormatException ex) { + ; // do nothing, the token was '-' which is legal if not a binding reaction + } + } else if (token.equals(VCML.SelectedParticle)){ + token = tokens.nextToken(); + String varName = token; + Variable var = mathDesc.getVariable(varName); + if (var instanceof ParticleVariable){ + particles.add((ParticleVariable)var); + }else{ + throw new MathFormatException("variable "+varName+" not a "+VCML.VolumeParticleVariable+" or "+VCML.MembraneParticleVariable); + } + } else if (token.equals(VCML.MacroscopicRateConstant)){ + Expression exp = MathFunctionDefinitions.fixFunctionSyntax(tokens); + particleRateDef = new MacroscopicRateConstant(exp); + }else if (token.equals(VCML.InteractionRadius)){ + Expression exp = MathFunctionDefinitions.fixFunctionSyntax(tokens); + particleRateDef = new InteractionRadius(exp); + } else if (token.equals(VCML.Action)){ + token = tokens.nextToken(); + String varName = token; + Variable var = mathDesc.getVariable(varName); + ParticleVariable particleVar = null; + if (var instanceof ParticleVariable){ + particleVar = (ParticleVariable)var; + }else{ + throw new MathFormatException("variable "+varName+" not a "+VCML.VolumeParticleVariable+" or "+VCML.MembraneParticleVariable); + } + token = tokens.nextToken(); + if (token.equals(VCML.CreateParticle)){ + actions.add(Action.createCreateAction(particleVar)); + }else if (token.equals(VCML.DestroyParticle)){ + actions.add(Action.createDestroyAction(particleVar)); + }else{ + throw new MathFormatException("unexpected command "+token+" within "+VCML.ParticleJumpProcess+" "+name); + } + } else if (token.equals(VCML.ProcessSymmetryFactor)){ + token = tokens.nextToken(); + symmetryFactor = new ProcessSymmetryFactor(Double.parseDouble(token)); + } + token = tokens.nextToken(); + } + LangevinParticleJumpProcess pjp = new LangevinParticleJumpProcess(name,particles,particleRateDef,actions,symmetryFactor); + pjp.setSubtype(subtype); + pjp.setTransitionCondition(transitionCondition); + pjp.setBondLength(bondLength); + return pjp; +} + +public void setSubtype(Subtype subtype) { + this.subtype = subtype; +} +public Subtype getSubtype() { + return subtype; +} +public void setTransitionCondition(TransitionCondition transitionCondition) { + this.transitionCondition = transitionCondition; +} +public TransitionCondition getTransitionCondition() { + return transitionCondition; +} +public void setBondLength(double bondLength) { + this.bondLength = bondLength; +} +public double getBondLength() { + return bondLength; +} + +} diff --git a/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleMolecularComponent.java b/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleMolecularComponent.java new file mode 100644 index 0000000000..2400b3d459 --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleMolecularComponent.java @@ -0,0 +1,188 @@ +package cbit.vcell.math; + +import java.beans.PropertyVetoException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.vcell.model.rbm.ComponentStateDefinition; +import org.vcell.util.CommentStringTokenizer; +import org.vcell.util.Compare; +import org.vcell.util.Coordinate; +import org.vcell.util.Matchable; +import org.vcell.util.springsalad.Colors; +import org.vcell.util.springsalad.IOHelp; +import org.vcell.util.springsalad.NamedColor; + +import cbit.vcell.model.Structure; + +@SuppressWarnings("serial") +public class LangevinParticleMolecularComponent extends ParticleMolecularComponent { + + private double fieldRadius = 1.0; + private double fieldDiffusionRate = 1.0; + private String fieldLocation = null; // feature or membrane name, identical to subdomain name + private Coordinate fieldCoordinate = new Coordinate(0,0,0); // double x,y,z; has distanceTo() + private NamedColor fieldColor = Colors.RED; + + + @SuppressWarnings("unused") + private LangevinParticleMolecularComponent() { // making it impossible to use the default constructor + super(null, null); + } + public LangevinParticleMolecularComponent(String id, String name) { + super(id, name); + } + + + @Override + public boolean compareEqual(Matchable obj) { + if (!(obj instanceof LangevinParticleMolecularComponent)) { + return false; + } + LangevinParticleMolecularComponent other = (LangevinParticleMolecularComponent)obj; + + if(false) { // TODO: compare everything that needs comparing + return false; + } + return super.compareEqual(obj); + } + + public String getVCML() { + StringBuffer buffer = new StringBuffer(); + buffer.append(" "+VCML.ParticleMolecularComponent + " " + getName()+" { "); + if (getComponentStateDefinitions().size()==0) { + buffer.append(" }"); + } else { + buffer.append("\n "+VCML.ParticleComponentRadius + " " + fieldRadius + ""); + buffer.append("\n "+VCML.ParticleComponentDiffusionRate + " " + fieldDiffusionRate + ""); + buffer.append("\n "+VCML.ParticleComponentLocation + " " + fieldLocation + ""); + buffer.append("\n "+VCML.ParticleComponentCoordinate + " " + fieldCoordinate + ""); + buffer.append("\n "+VCML.ParticleComponentColor + " " + fieldColor + ""); + for (ParticleComponentStateDefinition state : getComponentStateDefinitions()) { + String name = state.getName(); + buffer.append("\n "+VCML.ParticleComponentAllowableState + " " + name + ""); + } + buffer.append("\n "+"}"); + } + return buffer.toString(); + } + + public void read(CommentStringTokenizer tokens) throws MathFormatException { + String token = null; + token = tokens.nextToken(); + if (!token.equalsIgnoreCase(VCML.BeginBlock)){ + throw new MathFormatException("unexpected token "+token+" expecting "+VCML.BeginBlock); + } + while (tokens.hasMoreTokens()) { + token = tokens.nextToken(); + if (token.equalsIgnoreCase(VCML.EndBlock)) { + break; + } + if (token.equalsIgnoreCase(VCML.ParticleComponentAllowableState)) { + token = tokens.nextToken(); + String componentName = token; + if(!componentName.equals("*")) { + ParticleComponentStateDefinition pcsd = getComponentStateDefinition(componentName); + if(pcsd == null) { + pcsd = new ParticleComponentStateDefinition(componentName); + addComponentStateDefinition(pcsd); + } + } + continue; + } + if(token.equalsIgnoreCase(VCML.ParticleComponentRadius)) { + token = tokens.nextToken(); + Double radius = Double.parseDouble(token); + setRadius(radius); + continue; + } + if(token.equalsIgnoreCase(VCML.ParticleComponentDiffusionRate)) { + token = tokens.nextToken(); + Double dr = Double.parseDouble(token); + setDiffusionRate(dr); + continue; + } + if(token.equalsIgnoreCase(VCML.ParticleComponentLocation)) { + token = tokens.nextToken(); + String loc = token; + setLocation(loc); + continue; + } + if(token.equalsIgnoreCase(VCML.ParticleComponentCoordinate)) { + token = tokens.nextToken(); + String x = token; + x = x.substring(x.indexOf("="), x.length()); + token = tokens.nextToken(); + String y = token; + y = y.substring(y.indexOf("="), y.length()); + token = tokens.nextToken(); + String z = token; + z = z.substring(z.indexOf("="), z.length()); + Coordinate coordinate = new Coordinate(Double.parseDouble(x), Double.parseDouble(y), Double.parseDouble(z)); + setCoordinate(coordinate); + continue; + } + if(token.equalsIgnoreCase(VCML.ParticleComponentColor)) { + token = tokens.nextToken(); + NamedColor color = Colors.getColorByName(token); + setColor(color); + continue; + } + throw new MathFormatException("unexpected identifier "+token); + } + } + + public void writeType(StringBuilder sb) { + sb.append("TYPE: Name \"" + getName() + "\""); + sb.append(" Radius " + IOHelp.DF[5].format(getRadius()) + " D " + IOHelp.DF[3].format(getDiffusionRate()) + " Color " + getColor().getName()); + sb.append(" STATES "); + for (ParticleComponentStateDefinition state : getComponentStateDefinitions()) { + sb.append("\"" + state.getName() + "\"" + " "); + } + sb.append("\n"); + } + public void writeSite(StringBuilder sb, int index, String initialState) { + sb.append("SITE " + index + + " : " + getLocation() + " : Initial State '" + initialState + "'"); + sb.append("\n"); + sb.append(" "); + this.writeType(sb); // ex: TYPE: Name "Type2" Radius 1.00000 D 1.000 Color LIME STATES "State0" "State1" + sb.append(" " + "x " + IOHelp.DF[5].format(getCoordinate().getX()) + " y " + + IOHelp.DF[5].format(getCoordinate().getY()) + " z " + + IOHelp.DF[5].format(getCoordinate().getZ()) + " "); // ex: x 4.00000 y 4.00000 z 20.00000 + sb.append("\n"); + } + + public double getRadius() { + return fieldRadius; + } + public void setRadius(double fieldRadius) { + this.fieldRadius = fieldRadius; + } + public double getDiffusionRate() { + return fieldDiffusionRate; + } + public void setDiffusionRate(double fieldDiffusionRate) { + this.fieldDiffusionRate = fieldDiffusionRate; + } + public String getLocation() { + return fieldLocation; + } + public void setLocation(String fieldLocation) { + this.fieldLocation = fieldLocation; + } + public Coordinate getCoordinate() { + return fieldCoordinate; + } + public void setCoordinate(Coordinate fieldCoordinate) { + this.fieldCoordinate = fieldCoordinate; + } + public NamedColor getColor() { + return fieldColor; + } + public void setColor(NamedColor fieldColor) { + this.fieldColor = fieldColor; + } + +} diff --git a/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleMolecularType.java b/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleMolecularType.java new file mode 100644 index 0000000000..8dcd30406d --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/math/LangevinParticleMolecularType.java @@ -0,0 +1,132 @@ +package cbit.vcell.math; + +import java.beans.PropertyVetoException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.vcell.util.CommentStringTokenizer; +import org.vcell.util.Compare; +import org.vcell.util.Matchable; +import org.vcell.util.Pair; + +@SuppressWarnings("serial") +public class LangevinParticleMolecularType extends ParticleMolecularType { + + private Set> internalLinkSpec = new LinkedHashSet<> (); + + + public LangevinParticleMolecularType(String name) { + super(name); + } + + + @Override + public boolean compareEqual(Matchable obj) { + if(!(obj instanceof LangevinParticleMolecularType)) { + return false; + } + LangevinParticleMolecularType other = (LangevinParticleMolecularType)obj; + if (false) { + return false; // TODO: compare everything that needs comparing + } + return super.compareEqual(obj); + } + + public String getVCML() { + StringBuffer buffer = new StringBuffer(); + buffer.append(VCML.ParticleMolecularType + " " + getName() + " " + VCML.BeginBlock); + if (getComponentList().size() == 0 && getAnchorList().size() == 0) { + buffer.append(" "+VCML.EndBlock+"\n"); + } else { + if(internalLinkSpec.size() > 0) { + buffer.append("\n " + VCML.Links + " " + VCML.BeginBlock); + for(Pair pair : internalLinkSpec) { + buffer.append("\n " + pair.one.getName() + VCML.LinkSeparator + pair.two.getName()); + } + buffer.append("\n " + VCML.EndBlock); + } + for (ParticleMolecularComponent component : getComponentList()) { + if(!(component instanceof LangevinParticleMolecularComponent)) { + throw new RuntimeException("LangevinParticleMolecularType: Site instance must be LangevinParticleMolecularComponent"); + } + LangevinParticleMolecularComponent langevinComponent = (LangevinParticleMolecularComponent)component; + buffer.append("\n "+langevinComponent.getVCML()); + } + for(String anchor : getAnchorList()) { + buffer.append("\n " + VCML.ParticleMolecularTypeAnchor + " " + anchor); + } + buffer.append("\n"+VCML.EndBlock+"\n"); + } + return buffer.toString(); + } + + public void read(CommentStringTokenizer tokens) throws MathFormatException { + Set> linksAsStringsSet = new LinkedHashSet<> (); + Map lpmcMap = new LinkedHashMap<> (); + String token = null; + token = tokens.nextToken(); + if (!token.equalsIgnoreCase(VCML.BeginBlock)){ + throw new MathFormatException("unexpected token "+token+" expecting "+VCML.BeginBlock); + } + while (tokens.hasMoreTokens()) { + token = tokens.nextToken(); + if (token.equalsIgnoreCase(VCML.EndBlock)) { + break; + } + if (token.equalsIgnoreCase(VCML.Links)) { + token = tokens.nextToken(); + if (!token.equalsIgnoreCase(VCML.BeginBlock)){ + throw new MathFormatException("unexpected token "+token+" expecting "+VCML.BeginBlock); + } + while (tokens.hasMoreTokens()) { + token = tokens.nextToken(); + if (token.equalsIgnoreCase(VCML.EndBlock)) { + break; + } + token = tokens.nextToken(); + String one = token; + token = tokens.nextToken(); // the " :: " + token = tokens.nextToken(); + String two = token; + Pair pair = new Pair<> (one, two); + linksAsStringsSet.add(pair); // at this point we may not have yet the LangevinParticleMolecularComponent + } + } + if (token.equalsIgnoreCase(VCML.ParticleMolecularComponent)) { + token = tokens.nextToken(); + String molecularComponentName = token; + String id = getName() + "_" + molecularComponentName; + LangevinParticleMolecularComponent particleMolecularComponent = new LangevinParticleMolecularComponent(id, molecularComponentName); + particleMolecularComponent.read(tokens); + addMolecularComponent(particleMolecularComponent); + lpmcMap.put(molecularComponentName, particleMolecularComponent); + continue; + } else if(token.equalsIgnoreCase(VCML.ParticleMolecularTypeAnchor)) { + token = tokens.nextToken(); + String anchor = token; + getAnchorList().add(anchor); + continue; + } + throw new MathFormatException("unexpected identifier "+token); + } + for(Pair pair : linksAsStringsSet) { + LangevinParticleMolecularComponent one = lpmcMap.get(pair.one); + LangevinParticleMolecularComponent two = lpmcMap.get(pair.two); + Pair link = new Pair<> (one, two); + internalLinkSpec.add(link); + } + } + + public Set> getInternalLinkSpec() { + return internalLinkSpec; + } + public void setInternalLinkSpec(Set> internalLinkSpec) { + this.internalLinkSpec = internalLinkSpec; + } + +} diff --git a/vcell-core/src/main/java/cbit/vcell/math/MathDescription.java b/vcell-core/src/main/java/cbit/vcell/math/MathDescription.java index a0d1ca2f85..13261d72dd 100644 --- a/vcell-core/src/main/java/cbit/vcell/math/MathDescription.java +++ b/vcell-core/src/main/java/cbit/vcell/math/MathDescription.java @@ -1913,9 +1913,31 @@ public boolean isNonSpatialStoch() { } +@Override public boolean isRuleBased(){ - if (particleMolecularTypes.size()>0){ - return true; + if (particleMolecularTypes.size() > 0) { + if(!isLangevin()) { // in math description, isRuleBased() and isLangevin() are mutually exclusive + return true; + } else { + return false; + } + } + return false; +} + +@Override +public boolean isLangevin() { + Enumeration enum1 = getSubDomains(); + while (enum1.hasMoreElements()) { + SubDomain subDomain = enum1.nextElement(); + List particleJumpProcesses = subDomain.getParticleJumpProcesses(); + for(ParticleJumpProcess pjp : particleJumpProcesses) { + if(pjp instanceof LangevinParticleJumpProcess) { + return true; // the first jump process instance is enough to decide one way or another + } else { + return false; + } + } } return false; } @@ -3508,11 +3530,13 @@ public String toString() { public MathType getMathType() { - if (isNonSpatialStoch() || isSpatialStoch() || isSpatialHybrid()){ + if(isNonSpatialStoch() || isSpatialStoch() || isSpatialHybrid()) { return MathType.Stochastic; - } else if (isRuleBased()){ + } else if(isRuleBased() && !isLangevin()) { return MathType.RuleBased; - }else{ + } else if(isLangevin()) { + return MathType.SpringSaLaD; + } else { return MathType.Deterministic; } } diff --git a/vcell-core/src/main/java/cbit/vcell/math/ParticleJumpProcess.java b/vcell-core/src/main/java/cbit/vcell/math/ParticleJumpProcess.java index 442bfda4fc..6e49d63c01 100644 --- a/vcell-core/src/main/java/cbit/vcell/math/ParticleJumpProcess.java +++ b/vcell-core/src/main/java/cbit/vcell/math/ParticleJumpProcess.java @@ -25,11 +25,11 @@ public class ParticleJumpProcess implements org.vcell.util.Matchable,java.io.Serializable { private String processName = null; - private List particles = null; + protected List particles = null; private JumpProcessRateDefinition rateDefinition = null; - private List actions = null; + protected List actions = null; // private ArrayList processParticleMappings = null; - private ProcessSymmetryFactor processSymmetryFactor = null; + protected ProcessSymmetryFactor processSymmetryFactor = null; public static class ProcessSymmetryFactor implements Matchable, Serializable { double factor; diff --git a/vcell-core/src/main/java/cbit/vcell/math/ProblemRequirements.java b/vcell-core/src/main/java/cbit/vcell/math/ProblemRequirements.java index b44c06e11f..1f8111edb2 100644 --- a/vcell-core/src/main/java/cbit/vcell/math/ProblemRequirements.java +++ b/vcell-core/src/main/java/cbit/vcell/math/ProblemRequirements.java @@ -22,6 +22,8 @@ public interface ProblemRequirements { public abstract boolean isRuleBased(); + public abstract boolean isLangevin(); + public abstract boolean isMovingMembrane(); /** diff --git a/vcell-core/src/main/java/cbit/vcell/math/VCML.java b/vcell-core/src/main/java/cbit/vcell/math/VCML.java index 57dd17fc28..3076e8348d 100644 --- a/vcell-core/src/main/java/cbit/vcell/math/VCML.java +++ b/vcell-core/src/main/java/cbit/vcell/math/VCML.java @@ -213,6 +213,16 @@ public class VCML { // public final static String ProcessParticleTypeMapping = "ProcessParticleTypeMapping"; // particle type mapping (molecular pattern) // public final static String ProcessParticleComponentMapping = "ProcessParticleComponentMapping"; // particle type mapping (molecular component pattern) public final static String ProcessSymmetryFactor = "ProcessSymmetryFactor"; // for NFSim generated by BNG.exe + public final static String Subtype = "Subtype"; // particle Langevin / SpringSaLaD + public final static String TransitionCondition = "TransitionCondition"; // particle Langevin / SpringSaLaD + public final static String BondLength = "BondLength"; // particle Langevin / SpringSaLaD + public final static String Links = "Links"; // particle Langevin / SpringSaLaD + public final static String LinkSeparator = " :: "; // particle Langevin / SpringSaLaD + public final static String ParticleComponentRadius = "Radius"; // particle Langevin / SpringSaLaD + public final static String ParticleComponentDiffusionRate = "DiffusionRate"; // particle Langevin / SpringSaLaD + public final static String ParticleComponentLocation = "Location"; // particle Langevin / SpringSaLaD + public final static String ParticleComponentCoordinate = "Coordinate"; // particle Langevin / SpringSaLaD + public final static String ParticleComponentColor = "NamedColor"; // particle Langevin / SpringSaLaD public final static String ParticleMolecularComponent = "Component"; public final static String ParticleComponentAllowableState = "AllowableState"; diff --git a/vcell-core/src/main/java/cbit/vcell/model/Model.java b/vcell-core/src/main/java/cbit/vcell/model/Model.java index f4124c5cf3..d155c545be 100644 --- a/vcell-core/src/main/java/cbit/vcell/model/Model.java +++ b/vcell-core/src/main/java/cbit/vcell/model/Model.java @@ -3302,6 +3302,10 @@ public void propertyChange(java.beans.PropertyChangeEvent evt) { firePropertyChange(PROPERTY_NAME_MODEL_ENTITY_NAME, evt.getOldValue(), evt.getNewValue()); } + if(evt.getSource() instanceof MolecularType && evt.getPropertyName().equals(MolecularType.PROPERTY_NAME_COMPONENT_LIST)) { + // redirected message that serves a very narrow role specifically for springsalad applications + firePropertyChange(MolecularType.PROPERTY_NAME_COMPONENT_LIST, evt.getSource(), evt.getNewValue()); + } } @@ -3451,7 +3455,7 @@ private void refreshDiagrams() { } -public void removeStructure(Structure removedStructure) throws PropertyVetoException { +public void removeStructure(Structure removedStructure, boolean canRemoveLast) throws PropertyVetoException { if (removedStructure == null){ throw new RuntimeException("structure is null"); @@ -3459,6 +3463,9 @@ public void removeStructure(Structure removedStructure) throws PropertyVetoExcep if (!contains(removedStructure)){ throw new RuntimeException("structure "+removedStructure.getName()+" not found"); } + if(fieldStructures.length == 1 && canRemoveLast == false) { + throw new RuntimeException("Remove model compartment Error. Cannot remove the last compartment."); + } //Check that the feature is empty String prefix = "Remove model compartment Error\n\nStructure to be removed : '"+removedStructure.getName()+"' : "; @@ -4635,7 +4642,7 @@ public String isValidForStochApp() } public void removeObject(Object object) throws PropertyVetoException { - if(object instanceof Feature || object instanceof Membrane) { removeStructure((Structure) object); } + if(object instanceof Feature || object instanceof Membrane) { removeStructure((Structure) object, false); } else if(object instanceof ModelParameter) { removeModelParameter((ModelParameter) object); } else if(object instanceof ReactionStep) { removeReactionStep((ReactionStep) object); } else if(object instanceof Species) { removeSpecies((Species) object); } diff --git a/vcell-core/src/main/java/cbit/vcell/model/ReactionRule.java b/vcell-core/src/main/java/cbit/vcell/model/ReactionRule.java index cfeb6b2e3e..ac2fab7811 100644 --- a/vcell-core/src/main/java/cbit/vcell/model/ReactionRule.java +++ b/vcell-core/src/main/java/cbit/vcell/model/ReactionRule.java @@ -1052,44 +1052,6 @@ public void gatherIssues(IssueContext issueContext, List issueList) { } } - // check if there are disjoint patterns (molecules not explicitely connected through components) - for(ReactantPattern rp : reactantPatterns) { - SpeciesPattern sp = rp.getSpeciesPattern(); - if(sp.getMolecularTypePatterns().size() < 2) { - continue; // no point in looking since the pattern has a single molecule - } - boolean isDisjoint = false; - for(MolecularTypePattern mtp : sp.getMolecularTypePatterns()) { - if(mtp.isDisjoint()) { - isDisjoint = true; - break; // found an unconnected molecule in the species pattern - } - } - if(isDisjoint) { // tell the world - String message = "Found disjoint sets in a reactant pattern, as in A().B() with no explicit bond through components."; - String toolTip = "These type of patterns may make NFSim run slower or even fail. Is it highly advised to express this pattern with a different syntax."; - issueList.add(new Issue(this, issueContext, IssueCategory.Identifiers, message, toolTip, Issue.Severity.WARNING)); - } - } - for(ProductPattern pp : productPatterns) { - SpeciesPattern sp = pp.getSpeciesPattern(); - if(sp.getMolecularTypePatterns().size() < 2) { - continue; - } - boolean isDisjoint = false; - for(MolecularTypePattern mtp : sp.getMolecularTypePatterns()) { - if(mtp.isDisjoint()) { - isDisjoint = true; - break; - } - } - if(isDisjoint) { - String message = "Found disjoint sets in a product pattern, as in A().B() with no explicit bond through components."; - String toolTip = "These type of patterns may make NFSim run slower or even fail. Is it highly advised to express this pattern with a different syntax."; - issueList.add(new Issue(this, issueContext, IssueCategory.Identifiers, message, toolTip, Issue.Severity.WARNING)); - } - } - // match management issueContext = issueContext.newChildContext(ContextType.ReactionRule, this); Map matchedReactants = new LinkedHashMap(); diff --git a/vcell-core/src/main/java/cbit/vcell/model/Structure.java b/vcell-core/src/main/java/cbit/vcell/model/Structure.java index 63e16281c1..14c808c2b0 100644 --- a/vcell-core/src/main/java/cbit/vcell/model/Structure.java +++ b/vcell-core/src/main/java/cbit/vcell/model/Structure.java @@ -14,8 +14,11 @@ import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.io.Serializable; +import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.vcell.util.*; import org.vcell.util.Issue.IssueCategory; @@ -35,6 +38,19 @@ public abstract class Structure implements Serializable, ScopedSymbolTable, Matc public final static String TYPE_NAME_FEATURE = "Compartment"; public final static String TYPE_NAME_MEMBRANE = "Membrane"; + public enum SpringStructureEnum { // SpringSaLaD specific + Intracellular("Intracellular"), + Membrane("Membrane"), + Extracellular("Extracellular"); + + final public String columnName; + private SpringStructureEnum(String columnName) { + this.columnName = columnName; + } + } + public final static LinkedHashSet springStructureSet = new LinkedHashSet<> (Arrays.asList( + SpringStructureEnum.Intracellular.columnName, SpringStructureEnum.Membrane.columnName, SpringStructureEnum.Extracellular.columnName)); + private String fieldName = new String(); private String sbmlId = null; private String sbmlName = null; diff --git a/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java b/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java index 8ebbccdeed..654bb5fd92 100644 --- a/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java +++ b/vcell-core/src/main/java/cbit/vcell/resource/PropertyLoader.java @@ -250,6 +250,8 @@ public class PropertyLoader { public static final String vcellVcmlLocation = record("vcell.vcmlLocation", ValueType.DIR); + public static final String enableSpringSaLaD = record("vcell.enableSpringSaLaD", ValueType.BOOL); + /** * native library directory, server side */ diff --git a/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorService.java b/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorService.java index 052100ffe1..afa6687d57 100644 --- a/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorService.java +++ b/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorService.java @@ -14,8 +14,13 @@ import org.vcell.model.bngl.ParseException; +import cbit.image.ImageException; +import cbit.vcell.geometry.GeometryException; import cbit.vcell.mapping.BioNetGenUpdaterCallback; +import cbit.vcell.mapping.IllegalMappingException; +import cbit.vcell.mapping.MappingException; import cbit.vcell.parser.ExpressionBindingException; +import cbit.vcell.parser.ExpressionException; public interface BNGExecutorService { @@ -33,7 +38,7 @@ public static BNGExecutorService getInstanceOld(BNGInput bngInput, Long timeoutD public List getCallbacks(); - public BNGOutput executeBNG() throws BNGException, ParseException, PropertyVetoException, ExpressionBindingException; + public BNGOutput executeBNG() throws BNGException, ParseException, PropertyVetoException, ExpressionBindingException, ExpressionException, GeometryException, ImageException, IllegalMappingException, MappingException; public void stopBNG() throws Exception; diff --git a/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorServiceMultipass.java b/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorServiceMultipass.java index 73a9d4f988..5ee39ace5c 100644 --- a/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorServiceMultipass.java +++ b/vcell-core/src/main/java/cbit/vcell/server/bionetgen/BNGExecutorServiceMultipass.java @@ -32,6 +32,7 @@ import org.vcell.model.rbm.RbmUtils.BnglObjectConstructionVisitor; import org.vcell.util.Pair; +import cbit.image.ImageException; import cbit.vcell.biomodel.BioModel; import cbit.vcell.bionetgen.BNGComplexSpecies; import cbit.vcell.bionetgen.BNGMultiStateSpecies; @@ -40,7 +41,10 @@ import cbit.vcell.bionetgen.BNGReaction; import cbit.vcell.bionetgen.BNGSpecies; import cbit.vcell.bionetgen.BNGSpeciesComponent; +import cbit.vcell.geometry.GeometryException; import cbit.vcell.mapping.BioNetGenUpdaterCallback; +import cbit.vcell.mapping.IllegalMappingException; +import cbit.vcell.mapping.MappingException; import cbit.vcell.mapping.NetworkTransformer; import cbit.vcell.mapping.SimulationContext; import cbit.vcell.mapping.SimulationContext.NetworkGenerationRequirements; @@ -54,6 +58,7 @@ import cbit.vcell.model.ReactionRule; import cbit.vcell.model.Structure; import cbit.vcell.parser.ExpressionBindingException; +import cbit.vcell.parser.ExpressionException; public class BNGExecutorServiceMultipass implements BNGExecutorService, BioNetGenUpdaterCallback { private final static Logger lg = LogManager.getLogger(BNGExecutorServiceMultipass.class); @@ -114,7 +119,7 @@ public List getCallbacks() { } @Override - public BNGOutput executeBNG() throws BNGException, ParseException, PropertyVetoException, ExpressionBindingException { + public BNGOutput executeBNG() throws BNGException, ParseException, PropertyVetoException, ExpressionException, GeometryException, ImageException, IllegalMappingException, MappingException { this.startTime = System.currentTimeMillis(); long eltDoWork = 0; // elapsed time in doWork long eltExecBng = 0; // elapsed time executing bngl @@ -871,7 +876,7 @@ public long getStartTime() { // where each molecule has an extra Site with the compartments as possible States // a reserved name will be used for this Site // - private String preprocessInput(String cBngInputString) throws ParseException, PropertyVetoException, ExpressionBindingException { + private String preprocessInput(String cBngInputString) throws ParseException, PropertyVetoException, ExpressionException, GeometryException, ImageException, IllegalMappingException, MappingException { // take the cBNGL file (as string), parse it to recover the rules (we'll need them later) // and create the bngl string with the extra, fake site for the compartments @@ -895,7 +900,7 @@ private String preprocessInput(String cBngInputString) throws ParseException, Pr Structure struct = model.getStructure(0); if(struct != null) { try { - model.removeStructure(struct); + model.removeStructure(struct, true); } catch (PropertyVetoException e) { lg.error(e); } diff --git a/vcell-core/src/main/java/cbit/vcell/simdata/SimDataConstants.java b/vcell-core/src/main/java/cbit/vcell/simdata/SimDataConstants.java index 617864f768..a06814ba79 100644 --- a/vcell-core/src/main/java/cbit/vcell/simdata/SimDataConstants.java +++ b/vcell-core/src/main/java/cbit/vcell/simdata/SimDataConstants.java @@ -80,7 +80,11 @@ public interface SimDataConstants { public static final String NFSIM_INPUT_FILE_EXTENSION = ".nfsimInput"; public static final String NFSIM_OUTPUT_FILE_EXTENSION = ".gdat"; public static final String NFSIM_OUTPUT_LOG_EXTENSION = ".log"; - + + public static final String LANGEVIN_INPUT_FILE_EXTENSION = ".langevinInput"; + public static final String LANGEVIN_OUTPUT_FILE_EXTENSION = ".ida"; + public static final String LANGEVIN_OUTPUT_LOG_EXTENSION = ".log"; + public static final String SMOLDYN_INPUT_FILE_EXTENSION = ".smoldynInput"; public static final String SMOLDYN_OUTPUT_FILE_EXTENSION = ".smoldynOutput"; public static final String SMOLDYN_HIGH_RES_VOLUME_SAMPLES_EXTENSION = ".hrvs"; diff --git a/vcell-core/src/main/java/cbit/vcell/simdata/SimulationData.java b/vcell-core/src/main/java/cbit/vcell/simdata/SimulationData.java index 31b3ec4772..1bafef517b 100644 --- a/vcell-core/src/main/java/cbit/vcell/simdata/SimulationData.java +++ b/vcell-core/src/main/java/cbit/vcell/simdata/SimulationData.java @@ -14,15 +14,19 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Scanner; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.Vector; @@ -108,7 +112,7 @@ public enum SolverDataType MBSData, COMSOL } - + public static class AmplistorHelper{ private File userDirectory; private VCDataIdentifier ahvcDataId; @@ -117,7 +121,7 @@ public static class AmplistorHelper{ private TreeSet amplistorNotfoundSet = new TreeSet(); boolean bNonSpatial = false; private SolverDataType solverDataType = null; - + SimDataAmplistorInfo simDataAmplistorInfo; public AmplistorHelper(VCDataIdentifier argVCDataID, File primaryUserDir, File secondaryUserDir,SimDataAmplistorInfo simDataAmplistorInfo) throws FileNotFoundException{ this.simDataAmplistorInfo = simDataAmplistorInfo; @@ -353,6 +357,14 @@ public File getSimTaskXMLFile() { private Vector annotatedFunctionList = new Vector(); private Vector dataSetIdentifierList = new Vector(); + /** + * LangevinNoVis01 output file names + */ + private final String full_count_header = "FullCountData.csv"; + private final String full_state_count_header = "FullStateCountData.csv"; + private final String full_bond_count_header = "FullBondData.csv"; + private final String site_property_data_header = "SitePropertyData.csv"; + private final String clusters_time_header = "Clusters_Time_.csv"; private long logFileLastModified = 0; private long logFileLength = 0; // we check first job functions file for user defined functions in the parameter scan, @@ -893,9 +905,25 @@ private synchronized File getMeshFile() throws FileNotFoundException { * @return cbit.vcell.simdata.ODEDataBlock */ public synchronized ODEDataBlock getODEDataBlock() throws DataAccessException, IOException { - File file = getODEDataFile(); - long lastModified = file.lastModified(); ODESimData odeSimData = null; + File file = null; + //TODO: Implement conversion here + + // Check if Langevin solver is being run by checking if correct log file exists + String[] localSimDirFiles = amplistorHelper.userDirectory.list(); + String localSimDirPath = amplistorHelper.userDirectory.getPath(); + if (localSimDirFiles != null) { + for (String f : localSimDirFiles) { + if (f.endsWith(LANGEVIN_INPUT_FILE_EXTENSION) && f.contains(vcDataId.getID())) { + writeIdaFile(localSimDirPath + "\\" + vcDataId.getID() + ".langevinI_FOLDER\\data\\Run0\\", localSimDirPath, vcDataId.getID()); + break; + } + } + } + + file = getODEDataFile(); + + long lastModified = file.lastModified(); try { if (odeIdentifier.equals(ODE_DATA_IDENTIFIER)) { @@ -940,6 +968,166 @@ else if (odeIdentifier.equals(NFSIM_DATA_IDENTIFIER)) return new ODEDataBlock(odeDataInfo, odeSimData); } + /** + * Converts Langevin's output to a singular .IDA file in dir + * @param dataDir : The path to the folder which contains LangevinNoVis01's output files + * @param localSimDir: The path to the temp folder which contains logs and outputs + * @param vcDataId : The current simulation Job ID + */ + public void writeIdaFile(String dataDir, String localSimDir, String vcDataId) throws IOException { +// int cluster_time = 0; + // Initialize .ida file + String path = Paths.get(localSimDir, vcDataId + LANGEVIN_OUTPUT_FILE_EXTENSION).toString(); + FileWriter writer = new FileWriter(path, false); + + // Create a list with all the paths of all wanted csv files + String[] paths = new String[]{dataDir + full_bond_count_header, dataDir + full_count_header,dataDir + full_state_count_header, dataDir + site_property_data_header, dataDir + clusters_time_header}; + ArrayList data = new ArrayList<>(); + + // ArrayList that stores an arrayList for each file in paths + ArrayList> list = new ArrayList>(); + + // Loop through the path list + for (int i = 0; i < paths.length; i++) { + + data = readToArray(paths[i], i==0); + + if (data.size() > 0) { // Only add to list if there is data in the file + list.add(data); + } else { + System.out.println("No data in file: " + paths[i]); + } + } + // Loop through the rows by using the length we found + for (int i = 0; i < list.get(0).size(); i++) { + + // Loop through each array for that line and get the value at that row + for (ArrayList arrays : list) { + writer.write(arrays.get(i)); + } + writer.write("\n"); + } + writer.close(); + } + + /** + * @param path: path to the csv file that will be parsed and returned as an array of values + * @param isFirst: Wether or not this is the first of many files you are going to parse using this method + * @return Arraylist of Strings where each value corresponds with a row of data at a time step + * @throws FileNotFoundException + */ + public ArrayList readToArray(String path, boolean isFirst) throws FileNotFoundException { + boolean firstLine = true; + int lineNum = 0; + String headerLead = null; + ArrayList list = new ArrayList<>(); + boolean seq = false; + + if (path.endsWith(clusters_time_header)) { + list = readClustersToArray(path); + return list; + } + + try (Scanner scanner = new Scanner(new File(path))) { + while (scanner.hasNextLine()) { + // Returns one line with each column value being seperated by a "," + String line = scanner.nextLine(); + + // Special treatment for this file + if (path.endsWith(site_property_data_header)) { + // Get the headers from the first line of the file + if (firstLine) { + String[] words = line.split(","); + // The headerLead that will accompany the actual headers + headerLead = words[1] + "_Site" + words[3] + "_" + words[5] + "_"; + firstLine = false; + continue; // Continue as you are not going to add this line on its own + } + lineNum++; + + // Add the header leads to the actual headers (free, bound, etc) + if (lineNum == 1) { + // Index at , to ignore first column of data (time) + line = line.substring(line.indexOf(",")+1); + String[] items = line.split(","); + line = ""; + for (String item : items) { + line += headerLead + item + ":"; // remove space befo and add : after + } + } else { + // Get rid of the time column by splitting the sentences and removing first word + line = line.substring(line.indexOf(",") + 1); + } + line = line.replace(" ", "").replace(",", "\t"); + // If blank line, initiate seq mode which starts to append incoming lines onto already made list lines from index 0 + if (line.equals("")) { + seq = true; + firstLine = true; + lineNum = 0; + } else if (seq) { + list.set(lineNum - 1, list.get(lineNum - 1) + line); + } else { + list.add(line); + } + } else { + lineNum += 1; + if (!isFirst) { + // Get rid of the time aspect on files that are no the isFirst + line = line.substring(line.indexOf(",") + 1); + + } else if (lineNum==1){ //IsFirst and lineNum == 1 + line = "t:" + line.substring(line.indexOf(",") + 1); + } + if (lineNum == 1) { + line = cleanString(line, true); + } + line = cleanString(line, false); + list.add(line); + } + } + return list; + } + } + + public String cleanString(String line, boolean Num1) { + // clean differently based on if the line is part of a header or just numeric data + if (Num1) { + line = line.trim().replace(" : ", "_").replace(": ", ":").replace(" ", "_").replace(",", ":"); + } else { + line = line.replace(",", "\t"); + } + return line; + } + + public ArrayList readClustersToArray(String path) throws FileNotFoundException { + + File full = new File(path); + File dir = new File(full.getParent()); + ArrayList array = new ArrayList<>(); + + String[] file_paths = dir.list(); + for (String f: file_paths) { + if (f.contains("Clusters_Time_")) { + try(Scanner scanner = new Scanner(new File(dir, f))) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + line = line.replace(",",":"); + int index = line.indexOf(":"); + if (!(line.equals(""))) { + if (array.size() < 1) { + array.add(line.substring(0, index).replace(" ", "_")); // + array.add(line.substring(index + 1).trim()); + } else { + array.add(line.substring(index + 1)); + break; + } + } + } + } + } + } + return array; + } /** * This method was created in VisualAge. @@ -951,7 +1139,10 @@ private synchronized File getODEDataFile() throws DataAccessException { throw new DataAccessException("ODE data filename not read from logfile"); } File odeFile = amplistorHelper.getFile(dataFilenames[0]); - if (odeFile.exists()) { + + if (odeFile.getName().contains("complete. Elapsed time:")) { // Langevin's log file does not really function normally so just check if it is that case by seeing the contents of the log + return new File(amplistorHelper.userDirectory, vcDataId.getID() + ".ida"); + } else if (odeFile.exists()) { return odeFile; } throw new DataAccessException("no results are available yet, please wait and try again..."); @@ -1423,7 +1614,7 @@ public synchronized DataIdentifier[] getVarAndFunctionDataIdentifiers(OutputCont throw new DataAccessException(e.getMessage(), e); } } - + if (!isRulesData && !getIsODEData() && !bIsComsol && dataFilenames != null) { // read variables only when I have never read the file since variables don't change if (dataSetIdentifierList.size() == 0) { @@ -1712,13 +1903,13 @@ private synchronized void readLog(File logFile) throws FileNotFoundException, Da dataFilenames = new String[1]; dataFilenames[0] = xxx; } - else if (logfileContent.startsWith(SolverDataType.MBSData.name())) + else if (logfileContent.startsWith(SolverDataType.MBSData.name())) { StringTokenizer st = new StringTokenizer(logfileContent); if (st.hasMoreTokens()) { st.nextToken(); // skip the first line - + int numTimes = st.countTokens() / 3; dataTimes = new double[numTimes]; dataFilenames = new String[numTimes]; @@ -1733,9 +1924,9 @@ else if (logfileContent.startsWith(SolverDataType.MBSData.name())) index++; } } - indexPDEdataTimes( ); + indexPDEdataTimes(); } - else if (logfileContent.startsWith(SolverDataType.COMSOL.name())) + else if (logfileContent.startsWith(SolverDataType.COMSOL.name())) { StringTokenizer st = new StringTokenizer(logfileContent); if (st.hasMoreTokens()) @@ -1819,7 +2010,7 @@ private synchronized void readMesh(File meshFile,File membraneMeshMetricsFile) t meshFileLastModified = lastModified; } }else{ - throw new FileNotFoundException("mesh file "+meshFile.getPath()+" not found"); + //throw new FileNotFoundException("mesh file "+meshFile.getPath()+" not found"); } // diff --git a/vcell-core/src/main/java/cbit/vcell/solver/LangevinSimulationOptions.java b/vcell-core/src/main/java/cbit/vcell/solver/LangevinSimulationOptions.java new file mode 100644 index 0000000000..f8ae57016e --- /dev/null +++ b/vcell-core/src/main/java/cbit/vcell/solver/LangevinSimulationOptions.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package cbit.vcell.solver; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.Serializable; + +import org.vcell.util.CommentStringTokenizer; +import org.vcell.util.Compare; +import org.vcell.util.DataAccessException; +import org.vcell.util.Matchable; + +import cbit.vcell.math.VCML; + +public class LangevinSimulationOptions implements Serializable, Matchable, VetoableChangeListener { + + + protected int numOfTrials = 1; + protected int runIndex = 0; // run index, will result in Run0 (fillowed by Run1, 2,...) + protected double intervalSpring = 1.00E-9; // default: dt_spring: 1.00E-9 - from advanced + protected double intervalImage = 1.00E-4; // default: dt_image: 1.00E-4 - from advanced + + protected transient PropertyChangeSupport propertyChange; + protected transient VetoableChangeSupport vetoChange; + + public LangevinSimulationOptions() { + removeVetoableChangeListener(this); + addVetoableChangeListener(this); + } + + public LangevinSimulationOptions(LangevinSimulationOptions smoldynSimulationOptions) { + this(); // TODO: properly implement copy constructor + } + + public LangevinSimulationOptions(CommentStringTokenizer tokens) throws DataAccessException { + this(); + readVCML(tokens); // TODO: implement read + } + + public boolean compareEqual(Matchable obj) { + if (!(obj instanceof LangevinSimulationOptions)) { + return false; + } + LangevinSimulationOptions nfsimSimulationOptions = (LangevinSimulationOptions)obj; + + // TODO: finish this + return true; + } +// ----------------------------------------------------------------------------------- + + public int getRunIndex() { + return runIndex; + } + public int getNumOfTrials() { + return numOfTrials; + } + public double getIntervalSpring() { + return intervalSpring; + } + public double getIntervalImage() { + return intervalImage; + } + public final void setRunIndex(int newValue) { + this.runIndex = newValue; + } + public final void setNumOfTrials(int newValue) { + this.numOfTrials = newValue; + } + public final void setIntervalI(double newValue) { + this.intervalSpring = newValue; + } + public final void setIntervalImage(double newValue) { + this.intervalImage = newValue; + } + + public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + getPropertyChange().addPropertyChangeListener(listener); + } + + public synchronized void addVetoableChangeListener(java.beans.VetoableChangeListener listener) { + getVetoChange().addVetoableChangeListener(listener); + } + + private java.beans.PropertyChangeSupport getPropertyChange() { + if (propertyChange == null) { + propertyChange = new java.beans.PropertyChangeSupport(this); + }; + return propertyChange; + } + + private java.beans.VetoableChangeSupport getVetoChange() { + if (vetoChange == null) { + vetoChange = new java.beans.VetoableChangeSupport(this); + }; + return vetoChange; + } + + private void firePropertyChange(java.lang.String propertyName, java.lang.Object oldValue, java.lang.Object newValue) { + getPropertyChange().firePropertyChange(propertyName, oldValue, newValue); + } + + private void fireVetoableChange(java.lang.String propertyName, java.lang.Object oldValue, java.lang.Object newValue) throws PropertyVetoException { + getVetoChange().fireVetoableChange(propertyName, oldValue, newValue); + } + + public String getVCML() { + StringBuffer buffer = new StringBuffer(); +// buffer.append("\t" + VCML.NFSimSimulationOptions + " " + VCML.BeginBlock + "\n"); +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_observableComputationOff + " " + observableComputationOff + "\n"); +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_preventIntraBonds + " " + preventIntraBonds + "\n"); +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_aggregateBookkeeping + " " + aggregateBookkeeping + "\n"); +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_matchComplexes + " " + matchComplexes + "\n"); +// if (moleculeDistance != null) { +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_moleculeDistance + " " + moleculeDistance + "\n"); +// } +// if (maxMoleculesPerType != null) { +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_maxMoleculesPerType + " " + maxMoleculesPerType + "\n"); +// } +// if (equilibrateTime != null) { +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_equilibrateTime + " " + equilibrateTime + "\n"); +// } +// if (randomSeed != null) { +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_randomSeed + " " + randomSeed + "\n"); +// } +// buffer.append("\t\t" + VCML.NFSimSimulationOptions_NumOfTrials + " " + numOfTrials + "\n"); +// +// buffer.append("\t" + VCML.EndBlock + "\n"); + return buffer.toString(); + } + + public void readVCML(CommentStringTokenizer tokens) throws DataAccessException { +// String token = tokens.nextToken(); +// if (token.equalsIgnoreCase(VCML.SmoldynSimulationOptions)) { +// token = tokens.nextToken(); +// if (!token.equalsIgnoreCase(VCML.BeginBlock)) { +// throw new DataAccessException("unexpected token " + token + " expecting " + VCML.BeginBlock); +// } +// } +// while (tokens.hasMoreTokens()) { +// token = tokens.nextToken(); +// if (token.equalsIgnoreCase(VCML.EndBlock)) { +// break; +// } +// if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_observableComputationOff)) { +// token = tokens.nextToken(); +// observableComputationOff = Boolean.parseBoolean(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_moleculeDistance)) { +// token = tokens.nextToken(); +// moleculeDistance = new Integer(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_aggregateBookkeeping)) { +// token = tokens.nextToken(); +// aggregateBookkeeping = Boolean.parseBoolean(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_maxMoleculesPerType)) { +// token = tokens.nextToken(); +// maxMoleculesPerType = new Integer(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_equilibrateTime)) { +// token = tokens.nextToken(); +// equilibrateTime = new Integer(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_randomSeed)) { +// token = tokens.nextToken(); +// randomSeed = new Integer(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_preventIntraBonds)) { +// token = tokens.nextToken(); +// preventIntraBonds = Boolean.parseBoolean(token); +// } else if(token.equalsIgnoreCase(VCML.NFSimSimulationOptions_matchComplexes)) { +// token = tokens.nextToken(); +// matchComplexes = Boolean.parseBoolean(token); +// } else if (token.equalsIgnoreCase(VCML.NFSimSimulationOptions_NumOfTrials)) { +// token = tokens.nextToken(); +// int val2 = Integer.parseInt(token); +// if(val2 < 1 ) { +// throw new DataAccessException("unexpected token " + token + ", num of trials is requied to be at least 1. "); +// } else { +// numOfTrials = val2; +// } +// } else { +// throw new DataAccessException("unexpected identifier " + token); +// } +// } + } + + public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { + } + + public synchronized void removeVetoableChangeListener(java.beans.VetoableChangeListener listener) { + getVetoChange().removeVetoableChangeListener(listener); + } + + public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { + getPropertyChange().removePropertyChangeListener(listener); + } + + public void refreshDependencies() { + removeVetoableChangeListener(this); + addVetoableChangeListener(this); + } +} diff --git a/vcell-core/src/main/java/cbit/vcell/solver/Simulation.java b/vcell-core/src/main/java/cbit/vcell/solver/Simulation.java index 6eef2df2ad..51d2cca1de 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/Simulation.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/Simulation.java @@ -13,9 +13,15 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import org.vcell.model.rbm.ComponentStateDefinition; +import org.vcell.model.rbm.MolecularComponent; +import org.vcell.model.rbm.MolecularType; +import org.vcell.model.rbm.SpeciesPattern; import org.vcell.util.CommentStringTokenizer; import org.vcell.util.Compare; import org.vcell.util.DataAccessException; @@ -33,13 +39,21 @@ import org.vcell.util.document.Version; import org.vcell.util.document.Versionable; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.Kind; +import cbit.vcell.mapping.ReactionContext; +import cbit.vcell.mapping.ReactionRuleSpec; +import cbit.vcell.mapping.ReactionRuleSpec.Subtype; +import cbit.vcell.mapping.ReactionSpec; import cbit.vcell.mapping.SimulationContext; import cbit.vcell.mapping.SimulationContextEntity; +import cbit.vcell.mapping.SpeciesContextSpec; import cbit.vcell.math.MathCompareResults; import cbit.vcell.math.MathDescription; import cbit.vcell.math.MathException; import cbit.vcell.math.VCML; +import cbit.vcell.model.Model; +import cbit.vcell.model.SpeciesContext; import cbit.vcell.solver.SolverDescription.SolverFeature; /** * Specifies the problem to be solved by a solver. @@ -104,6 +118,120 @@ public class Simulation implements Versionable, Matchable, java.beans.VetoableCh private boolean fieldIsDirty = false; private String fieldImportedTaskID; + public static class Counters { // SpringSaLaD post processing counters, structure to be determined, will be moved appropriately + + public static void writeMoleculeCounters(SimulationContext simContext, StringBuilder sb) { + if(simContext == null) { + sb.append("\n"); + return; + } + if(simContext.getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + Model model = simContext.getBioModel().getModel(); + SpeciesContext[] speciesContexts = model.getSpeciesContexts(); + for(SpeciesContext sc : speciesContexts) { + if(SpeciesContextSpec.SourceMoleculeString.equals(sc.getName()) || SpeciesContextSpec.SinkMoleculeString.equals(sc.getName())) { + continue; // skip the Source and the Sink molecules (use in Creation / Destruction reactions) + } + SpeciesPattern sp = sc.getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns() == null || sp.getMolecularTypePatterns().isEmpty()) { + continue; + } + // Each MolecularType can be used for only one SpeciesContext + MolecularType mt = sp.getMolecularTypePatterns().get(0).getMolecularType(); + sb.append("'").append(mt.getName()).append("' : ") + .append("Measure Total Free Bound"); + sb.append("\n"); + } + } + + public static void writeStateCounters(SimulationContext simContext, StringBuilder sb) { + if(simContext == null) { + sb.append("\n"); + return; + } + if(simContext.getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + Model model = simContext.getBioModel().getModel(); + SpeciesContext[] speciesContexts = model.getSpeciesContexts(); + for(SpeciesContext sc : speciesContexts) { + SpeciesPattern sp = sc.getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns() == null || sp.getMolecularTypePatterns().isEmpty()) { + continue; + } + MolecularType mt = sp.getMolecularTypePatterns().get(0).getMolecularType(); + List mcList = mt.getComponentList(); + for(MolecularComponent mc : mcList) { + List csdList = mc.getComponentStateDefinitions(); + for(ComponentStateDefinition csd : csdList) { + sb.append("'").append(mt.getName()).append("' : ") + .append("'").append(mc.getName()).append("' : ") + .append("'").append(csd.getName()).append("' : ") + .append("Measure Total Free Bound"); + sb.append("\n"); + } + } + } + } + + public static void writeBondCounters(SimulationContext simContext, StringBuilder sb) { + if(simContext == null) { + sb.append("\n"); + return; + } + if(simContext.getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + ReactionContext reactionContext = simContext.getReactionContext(); + ReactionRuleSpec[] reactionRuleSpecs = reactionContext.getReactionRuleSpecs(); + for(ReactionRuleSpec rrs : reactionRuleSpecs) { + if(rrs.isExcluded()) { + continue; + } + Map analysisResults = new LinkedHashMap<> (); + rrs.analizeReaction(analysisResults); + Subtype subtype = rrs.getSubtype(analysisResults); + if(subtype == ReactionRuleSpec.Subtype.BINDING) { + sb.append("'").append(rrs.getReactionRule().getName()).append("' : ") // was ("' : '") + .append("Counted"); + sb.append("\n"); + } + } + } + + public static void writeSitePropertyCounters(SimulationContext simContext, StringBuilder sb) { + if(simContext == null) { + sb.append("\n"); + return; + } + if(simContext.getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + Model model = simContext.getBioModel().getModel(); + SpeciesContext[] speciesContexts = model.getSpeciesContexts(); + for(SpeciesContext sc : speciesContexts) { + SpeciesPattern sp = sc.getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns() == null || sp.getMolecularTypePatterns().isEmpty()) { + continue; + } + MolecularType mt = sp.getMolecularTypePatterns().get(0).getMolecularType(); + List mcList = mt.getComponentList(); + for(MolecularComponent mc : mcList) { + sb.append("'").append(mt.getName()).append("' ") + .append("Site " + (mc.getIndex()-1)).append(" : ") + .append("Track Properties true"); + sb.append("\n"); + } + } + } + } + private Simulation( ) { } /** @@ -419,6 +547,9 @@ public boolean isSpatial() { // } //} +// this doesn't seem to be called anywhere, we deprecate it (also, it doesn't know about Feature_Springs) +// jul 2023 danv +@Deprecated public Set getRequiredFeatures() { Set requiredFeatures = new HashSet(); final MathDescription md = getMathDescription(); @@ -950,7 +1081,6 @@ public String getImportedTaskID() { return fieldImportedTaskID; } - public static final String typeName = "Simulation"; @Override public String getDisplayName() { diff --git a/vcell-core/src/main/java/cbit/vcell/solver/SolverDescription.java b/vcell-core/src/main/java/cbit/vcell/solver/SolverDescription.java index 17c8e353d9..d35fc5e25b 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/SolverDescription.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/SolverDescription.java @@ -127,9 +127,18 @@ public enum SolverDescription { false), NFSim(TimeStep.VARIABLE, ErrorTol.NO, TimeSpecCreated.UNIFORM, "NFSim","NFSim (Network Free Simulator)","NFSim", - SolverLongDesc.NFSIM, 1,SupportedTimeSpec.UNIFORM, - new SolverFeature[]{SolverFeature.Feature_NonSpatial, SolverFeature.Feature_Rulebased}, - SolverExecutable.NFSIM, "KISAO:0000263", false), + SolverLongDesc.NFSIM, 1,SupportedTimeSpec.UNIFORM, + new SolverFeature[]{SolverFeature.Feature_NonSpatial, SolverFeature.Feature_Rulebased}, + SolverExecutable.NFSIM, "KISAO:0000263", false), + + Langevin(TimeStep.CONSTANT, ErrorTol.NO, TimeSpecCreated.UNIFORM, "Langevin", "LangevinNoVis","Langevin", + SolverLongDesc.LANGEVIN, 1,SupportedTimeSpec.UNIFORM, + new SolverFeature[]{ + SolverFeature.Feature_Spatial, + SolverFeature.Feature_Stochastic, + SolverFeature.Feature_Rulebased, + SolverFeature.Feature_Springs}, + SolverExecutable.LANGEVIN, "KISAO:0000263", false), // TODO: find the right KISAO Comsol(TimeStep.VARIABLE,ErrorTol.NO,TimeSpecCreated.DEFAULT,"Comsol","Comsol Multiphysics","Comsol", SolverLongDesc.COMSOL,1,SupportedTimeSpec.DEFAULT_UNIFORM, @@ -178,6 +187,7 @@ public enum SolverFeature { Feature_Parallel("Parallel execution"), Feature_Hybrid("Hybrid: both Deterministic and Stochastic"), Feature_Moving("Moving Membrane"), + Feature_Springs("Spring connected Sites"), ; private final String name; @@ -241,6 +251,11 @@ public final String getName() { new SolverFeature[] { SolverFeature.Feature_NonSpatial, SolverFeature.Feature_Rulebased }, new SupportedProblemRequirements() { public boolean supports(ProblemRequirements desc) { return desc.isRuleBased(); }}, NFSim,100); + + public static final SolverFeatureSet LangevinFeatureSet = new SolverFeatureSet ( + new SolverFeature[] { SolverFeature.Feature_Spatial, SolverFeature.Feature_Rulebased, SolverFeature.Feature_Springs }, + new SupportedProblemRequirements() { public boolean supports(ProblemRequirements desc) { return (desc.isLangevin()); }}, + Langevin,200); /* * Non-spatial solvers @@ -688,6 +703,11 @@ public boolean isNFSimSolver() return this == NFSim; } + public boolean isLangevinSolver() + { + return this == Langevin; + } + public boolean isDeprecated() { return this.deprecated; } diff --git a/vcell-core/src/main/java/cbit/vcell/solver/SolverExecutable.java b/vcell-core/src/main/java/cbit/vcell/solver/SolverExecutable.java index b6ad8c46d8..62cf45fe49 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/SolverExecutable.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/SolverExecutable.java @@ -16,6 +16,7 @@ public enum SolverExecutable { Hybrid_Mil_Adaptive("Hybrid_MIL_Adaptive" ), Smoldyn("smoldyn" ), NFSIM("NFsim"), + LANGEVIN("LangevinNoVis"), MOVING_B("MovingBoundary") ; diff --git a/vcell-core/src/main/java/cbit/vcell/solver/SolverLongDesc.java b/vcell-core/src/main/java/cbit/vcell/solver/SolverLongDesc.java index ddfbbfeee0..79960ae175 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/SolverLongDesc.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/SolverLongDesc.java @@ -461,6 +461,15 @@ interface SolverLongDesc { "
  • " + Description_Stochastic_RANDOM_SEED + "
  • " + ""; + static final String LANGEVIN = + "" + + "

    DISPLAY_LABEL_TOKEN

    " + + "LangevinNoVis is a free, open-source, particle-based, stochastic, simulator platform suitable " + + "for modeling mesoscopic systems, which are far too large to be modeled with molecular dynamics " + + "but which require more detail than obtainable with macroscopic continuum models. Molecules are " + + "modeled as a collection of impenetrable spheres (called “sites”) linked by stiff springs." + + ""; + static final String COMSOL = "" + "

    DISPLAY_LABEL_TOKEN

    " + diff --git a/vcell-core/src/main/java/cbit/vcell/solver/SolverTaskDescription.java b/vcell-core/src/main/java/cbit/vcell/solver/SolverTaskDescription.java index 10cbd21f82..63ab5db257 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/SolverTaskDescription.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/SolverTaskDescription.java @@ -23,6 +23,8 @@ import org.vcell.util.DataAccessException; import org.vcell.util.Matchable; +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.math.Constant; import cbit.vcell.math.MathDescription; import cbit.vcell.math.MathFunctionDefinitions; @@ -49,6 +51,7 @@ public class SolverTaskDescription implements Matchable, java.beans.PropertyChan public static final String PROPERTY_STOP_AT_SPATIALLY_UNIFORM_ERROR_TOLERANCE = "stopAtSpatiallyUniformErrorTolerance"; public static final String PROPERTY_SMOLDYN_SIMULATION_OPTIONS = "smoldynSimulationOptions"; public static final String PROPERTY_NFSIM_SIMULATION_OPTIONS = "nfsimSimulationOptions"; + public static final String PROPERTY_LANGEVIN_SIMULATION_OPTIONS = "langevinSimulationOptions"; public static final String PROPERTY_SUNDIALS_SOLVER_OPTIONS = "sundialsSolverOptions"; // Or TASK_NONE for use as a default? @@ -74,6 +77,7 @@ public class SolverTaskDescription implements Matchable, java.beans.PropertyChan private boolean bSerialParameterScan = false; private SmoldynSimulationOptions smoldynSimulationOptions = null; private NFsimSimulationOptions nfsimSimulationOptions = null; + private LangevinSimulationOptions langevinSimulationOptions = null; private SundialsPdeSolverOptions sundialsPdeSolverOptions = null; private ChomboSolverSpec chomboSolverSpec = null; private MovingBoundarySolverOptions movingBoundarySolverOptions = null; @@ -177,6 +181,15 @@ public SolverTaskDescription(Simulation simulation, SolverTaskDescription solver } else { nfsimSimulationOptions = null; } + if (simulation.getMathDescription().isLangevin()) { + if(solverTaskDescription.langevinSimulationOptions != null) { + langevinSimulationOptions = new LangevinSimulationOptions(solverTaskDescription.langevinSimulationOptions); + } else { + langevinSimulationOptions = new LangevinSimulationOptions(); + } + } else { + langevinSimulationOptions = null; + } if (fieldSolverDescription.equals(SolverDescription.SundialsPDE)) { sundialsPdeSolverOptions = new SundialsPdeSolverOptions(solverTaskDescription.sundialsPdeSolverOptions); } else { @@ -310,6 +323,9 @@ public boolean compareEqual(Matchable object) { if (!Compare.isEqualOrNull(nfsimSimulationOptions,solverTaskDescription.nfsimSimulationOptions)) { return false; } + if (!Compare.isEqualOrNull(langevinSimulationOptions,solverTaskDescription.langevinSimulationOptions)) { + return false; + } if (!Compare.isEqualOrNull(movingBoundarySolverOptions,solverTaskDescription.movingBoundarySolverOptions)) { return false; } @@ -604,6 +620,9 @@ public String getVCML() { if (nfsimSimulationOptions != null) { buffer.append(nfsimSimulationOptions.getVCML()); } + if (langevinSimulationOptions != null) { + buffer.append(langevinSimulationOptions.getVCML()); + } if (smoldynSimulationOptions != null) { buffer.append(smoldynSimulationOptions.getVCML()); } @@ -706,6 +725,11 @@ public void propertyChange(java.beans.PropertyChangeEvent evt) { nfsimSimulationOptions = new NFsimSimulationOptions(); } } + if (solverDescription.isLangevinSolver()) { + if (langevinSimulationOptions == null) { + langevinSimulationOptions = new LangevinSimulationOptions(); + } + } if (solverDescription.isChomboSolver()) { if (chomboSolverSpec == null && getSimulation() != null) { chomboSolverSpec = new ChomboSolverSpec(ChomboSolverSpec.getDefaultMaxBoxSize(getSimulation().getMathDescription().getGeometry().getDimension())); @@ -1161,6 +1185,9 @@ public void refreshDependencies() { if(nfsimSimulationOptions != null) { nfsimSimulationOptions.refreshDependencies(); } + if(langevinSimulationOptions != null) { + langevinSimulationOptions.refreshDependencies(); + } } /** @@ -1436,6 +1463,9 @@ public final SmoldynSimulationOptions getSmoldynSimulationOptions() { public final NFsimSimulationOptions getNFSimSimulationOptions() { return nfsimSimulationOptions; } + public final LangevinSimulationOptions getLangevinSimulationOptions() { + return langevinSimulationOptions; + } public final SundialsPdeSolverOptions getSundialsPdeSolverOptions() { return sundialsPdeSolverOptions; @@ -1457,6 +1487,14 @@ public final void setNFSimSimulationOptions(NFsimSimulationOptions nfsimSimulati } } + public final void setLangevinSimulationOptions(LangevinSimulationOptions langevinSimulationOptions) { + if (!Matchable.areEqual(this.langevinSimulationOptions,langevinSimulationOptions)) { + LangevinSimulationOptions oldValue = this.langevinSimulationOptions; + this.langevinSimulationOptions = langevinSimulationOptions; + firePropertyChange(PROPERTY_LANGEVIN_SIMULATION_OPTIONS, oldValue, langevinSimulationOptions); + } + } + public final void setSundialsPdeSolverOptions(SundialsPdeSolverOptions sundialsPdeSolverOptions) { if (!Matchable.areEqual(this.sundialsPdeSolverOptions,sundialsPdeSolverOptions) ) { SundialsPdeSolverOptions oldValue = this.sundialsPdeSolverOptions; @@ -1525,6 +1563,35 @@ public void setMovingBoundarySolverOptions(MovingBoundarySolverOptions mb) { this.movingBoundarySolverOptions = mb; } + public void writeData(StringBuilder sb) { // SpringSaLaD exporting the time information + if(!(fieldSimulation.getSimulationOwner() instanceof SimulationContext)) { + sb.append("\n"); + return; + } + SimulationContext sc = (SimulationContext)fieldSimulation.getSimulationOwner(); + if(sc.getApplicationType() != Application.SPRINGSALAD) { + sb.append("\n"); + return; + } + + // ending time is editable, starting time is non-editable, always 0 + double totalTime = getTimeBounds().getEndingTime() - getTimeBounds().getStartingTime(); + sb.append("Total time: " + totalTime); // TODO: for langevin, initialize to 1.00E-2 + sb.append("\n"); + + double defaultTimeStep = getTimeStep().getDefaultTimeStep(); + sb.append("dt: " + defaultTimeStep); // TODO: initialize to 1.00E-8 + sb.append("\n"); + + if(!(getOutputTimeSpec() instanceof UniformOutputTimeSpec)) { + throw new RuntimeException("Output interval must be uniform"); + } + UniformOutputTimeSpec uots = (UniformOutputTimeSpec)getOutputTimeSpec(); + double outputInterval = uots.getOutputTimeStep(); + sb.append("dt_data: " + outputInterval); // TODO: initialize to 1.00E-4 + sb.append("\n"); + } + //double calculateBindingRadius(ParticleJumpProcess pjp, SubDomain subDomain) throws Exception //{ // double minRadius = 1e8; diff --git a/vcell-core/src/main/java/cbit/vcell/solver/server/SolverFactory.java b/vcell-core/src/main/java/cbit/vcell/solver/server/SolverFactory.java index 99a0f107c5..c7c3791f39 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/server/SolverFactory.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/server/SolverFactory.java @@ -15,6 +15,7 @@ import java.util.Map; import org.vcell.solver.comsol.ComsolSolver; +import org.vcell.solver.langevin.LangevinSolver; import org.vcell.solver.nfsim.NFSimSolver; import org.vcell.solver.smoldyn.SmoldynSolver; @@ -90,6 +91,7 @@ public Solver make(SimulationTask t, File userDir, File parallelWorkingDir, bool FACTORY.put(SolverDescription.HybridMilAdaptive , (t,d,pwd,m) -> new HybridSolver(t, d,HybridSolver.AdaptiveMilsteinIntegrator,m) ); FACTORY.put(SolverDescription.Smoldyn, (t,d,pwd,m) -> new SmoldynSolver(t, d,m) ); FACTORY.put(SolverDescription.NFSim, (t,d,pwd,m) -> new NFSimSolver(t, d,m) ); + FACTORY.put(SolverDescription.Langevin, (t,d,pwd,m) -> new LangevinSolver(t, d,m) ); FACTORY.put(SolverDescription.MovingBoundary, (t,d,pwd,m) -> new MovingBoundarySolver(t, d,m) ); FACTORY.put(SolverDescription.Comsol, (t,d,pwd,m) -> new ComsolSolver(t, d) ); } diff --git a/vcell-core/src/main/java/cbit/vcell/solvers/AbstractCompiledSolver.java b/vcell-core/src/main/java/cbit/vcell/solvers/AbstractCompiledSolver.java index 07ae6d3713..3db8cbd954 100644 --- a/vcell-core/src/main/java/cbit/vcell/solvers/AbstractCompiledSolver.java +++ b/vcell-core/src/main/java/cbit/vcell/solvers/AbstractCompiledSolver.java @@ -350,7 +350,12 @@ public Vector createFunctionList() { public void writeFunctionsFile() { // ** Dumping the functions of a simulation into a '.functions' file. String functionFileName = getBaseName() + FUNCTIONFILE_EXTENSION; - Vector funcList = createFunctionList(); + Vector funcList = null; + + // Create an empty function file if using langeVinSolver + if (!simTask.getSimulation().getSolverTaskDescription().getSolverDescription().isLangevinSolver()) { + funcList = createFunctionList(); + } //Try to save existing user defined functions FunctionFileGenerator functionFileGenerator = new FunctionFileGenerator(functionFileName, funcList); diff --git a/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java b/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java index f3b6ba9e9d..5db5d3b36c 100644 --- a/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java +++ b/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java @@ -132,7 +132,8 @@ public class XMLTags { public final static String VarNameAttrTag = "VarName"; //stoch public final static String OperationAttrTag = "Operation"; //stoch public final static String StochAttrTag = "Stochastic"; //stoch , used with simulationspec Tag as an attribute - public static final String RuleBasedAttrTag = "RuleBased"; //rule-based , used with simulationspec Tag as an attribute + public static final String RuleBasedAttrTag = "RuleBased"; // rule-based , used with simulationspec Tag as an attribute + public static final String SpringSaLaDAttrTag = "SpringSaLaD"; // springsalad application, used with simulationspec Tag as an attribute public final static String ConcentrationAttrTag = "UseConcentration"; //used for stochastic application. store initial condition by concentration or number of particles. public final static String RandomizeInitConditionTag = "RandomizeInitCondition"; //used for stochastic application. store boolean for randomizing initial condition public final static String InsufficientIterationsTag = "InsufficientIterations"; // used for flattening rule based reactions @@ -849,4 +850,27 @@ public class XMLTags { public final static String RedistributionFrequencyTag = "RedistributionFrequency"; public final static String ExtrapolationMethodTag = "ExtrapolationMethod"; + // SpringSaLaD tags + public final static String SiteAttributesMapTag = "SiteAttributesMap"; + public final static String SiteAttributesSpecTag = "SiteAttributesSpec"; + public final static String SiteRefAttrTag = "SiteRef"; + public final static String MoleculeRefAttrTag = "MoleculeRef"; + public final static String SiteLocationRefAttrTag = "SiteLocationRef"; + public final static String SiteRadiusAttrTag = "SiteRadius"; + public final static String SiteDiffusionAttrTag = "SiteDiffusion"; + public final static String SiteColorAttrTag = "SiteColor"; + public final static String SiteCoordXAttrTag = "SiteCoordX"; + public final static String SiteCoordYAttrTag = "SiteCoordY"; + public final static String SiteCoordZAttrTag = "SiteCoordZ"; + + public final static String InternalLinkSpecTag = "InternalLinkSpec"; + public final static String SiteOneRefAttrTag = "SiteOneRef"; + public final static String SiteTwoRefAttrTag = "SiteTwoRef"; + + public final static String BondLengthAttrTag = "BondLength"; + public final static String SubTypeAttrTag = "SubType"; + public final static String TransitionConditionAttrTag = "TransitionCondition"; + + + } diff --git a/vcell-core/src/main/java/cbit/vcell/xml/Xmlproducer.java b/vcell-core/src/main/java/cbit/vcell/xml/Xmlproducer.java index b59b3f99e4..3d200a0484 100644 --- a/vcell-core/src/main/java/cbit/vcell/xml/Xmlproducer.java +++ b/vcell-core/src/main/java/cbit/vcell/xml/Xmlproducer.java @@ -19,9 +19,12 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import java.util.Vector; import org.apache.commons.lang3.mutable.Mutable; @@ -31,6 +34,8 @@ import org.jdom.Document; import org.jdom.Element; import org.jdom.Namespace; +import org.jdom.output.Format; +import org.jdom.output.XMLOutputter; import org.sbpax.schemas.util.DefaultNameSpaces; import org.vcell.chombo.ChomboSolverSpec; import org.vcell.chombo.RefinementRoi; @@ -121,8 +126,10 @@ import cbit.vcell.mapping.MicroscopeMeasurement.ConvolutionKernel; import cbit.vcell.mapping.MicroscopeMeasurement.GaussianConvolutionKernel; import cbit.vcell.mapping.MicroscopeMeasurement.ProjectionZKernel; +import cbit.vcell.mapping.MolecularInternalLinkSpec; import cbit.vcell.mapping.ParameterContext.LocalParameter; import cbit.vcell.mapping.ParameterContext.ParameterRoleEnum; +import cbit.vcell.mapping.ReactionRuleSpec.TransitionCondition; import cbit.vcell.mapping.RateRule; import cbit.vcell.mapping.ReactionContext; import cbit.vcell.mapping.ReactionRuleSpec; @@ -130,6 +137,7 @@ import cbit.vcell.mapping.SimulationContext; import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.SimulationContextParameter; +import cbit.vcell.mapping.SiteAttributesSpec; import cbit.vcell.mapping.SpeciesContextSpec; import cbit.vcell.mapping.StructureMapping; import cbit.vcell.mapping.TotalCurrentClampStimulus; @@ -1483,7 +1491,7 @@ private Element getXML(ReactionContext param) { //Add SpeciesContextSpecs SpeciesContextSpec[] array = param.getSpeciesContextSpecs(); for (int i =0; i0){ - reactioncontext.addContent( getXML(reactionRuleArray) ); + if (reactionRuleArray.length>0) { + reactioncontext.addContent( getXML(reactionRuleArray, param.getSimulationContext()) ); } return reactioncontext; @@ -1517,13 +1525,28 @@ private Element getXML(ReactionSpec param) { } //For rateRules in SimulationContext -public Element getXML(ReactionRuleSpec[] reactionRuleSpecs) { +public Element getXML(ReactionRuleSpec[] reactionRuleSpecs, SimulationContext simContext) { Element reactionRuleSpecsElement = new Element(XMLTags.ReactionRuleSpecsTag); for (ReactionRuleSpec reactionRuleSpec : reactionRuleSpecs){ Element reactionRuleSpecElement = new Element(XMLTags.ReactionRuleSpecTag); reactionRuleSpecElement.setAttribute(XMLTags.ReactionRuleRefAttrTag, mangle(reactionRuleSpec.getReactionRule().getName())); reactionRuleSpecElement.setAttribute(XMLTags.ReactionRuleMappingAttrTag, mangle(reactionRuleSpec.getReactionRuleMapping().getDatabaseName())); - + if(Application.SPRINGSALAD == simContext.getApplicationType()) { + reactionRuleSpecElement.setAttribute(XMLTags.BondLengthAttrTag, Double.toString(reactionRuleSpec.getFieldBondLength())); + // + // the next 2 attributes are sent only for debugging purposes, they are derived attributes and should be calculated at needed + // + Map analysisResults = new LinkedHashMap<> (); + reactionRuleSpec.analizeReaction(analysisResults); + ReactionRuleSpec.Subtype st = reactionRuleSpec.getSubtype(analysisResults); + reactionRuleSpecElement.setAttribute(XMLTags.SubTypeAttrTag, st.columnName); + if(ReactionRuleSpec.Subtype.TRANSITION == st) { + TransitionCondition tc = reactionRuleSpec.getTransitionCondition(analysisResults); + if(tc != null) { + reactionRuleSpecElement.setAttribute(XMLTags.TransitionConditionAttrTag, tc.vcellName); + } + } + } reactionRuleSpecsElement.addContent(reactionRuleSpecElement); } @@ -1544,19 +1567,24 @@ public Element getXML(SimulationContext param, BioModel bioModel) throws XmlPars String name = mangle(param.getName()); simulationcontext.setAttribute(XMLTags.NameAttrTag, name); //set isStoch, isUsingConcentration attributes - if (applicationType == Application.NETWORK_STOCHASTIC) - { + if (applicationType == Application.NETWORK_STOCHASTIC) { simulationcontext.setAttribute(XMLTags.StochAttrTag, "true"); setBooleanAttribute(simulationcontext, XMLTags.ConcentrationAttrTag, param.isUsingConcentration()); // write out 'randomizeInitConditin' flag only if non-spatial stochastic simContext if(param.getGeometry().getDimension() == 0) { setBooleanAttribute(simulationcontext, XMLTags.RandomizeInitConditionTag,param.isRandomizeInitCondition()); } - } - else - { + setBooleanAttribute(simulationcontext, XMLTags.SpringSaLaDAttrTag, false); + } else if(applicationType == Application.SPRINGSALAD) { + boolean isRandomizeInitCondition = param.isRandomizeInitCondition(); + boolean isUsingConcentration = param.isUsingConcentration(); + simulationcontext.setAttribute(XMLTags.StochAttrTag, "false"); + setBooleanAttribute(simulationcontext, XMLTags.ConcentrationAttrTag, isUsingConcentration); + setBooleanAttribute(simulationcontext, XMLTags.SpringSaLaDAttrTag, true); + } else { simulationcontext.setAttribute(XMLTags.StochAttrTag, "false"); simulationcontext.setAttribute(XMLTags.ConcentrationAttrTag, "true"); + setBooleanAttribute(simulationcontext, XMLTags.SpringSaLaDAttrTag, false); } final boolean ruleBased = param.getApplicationType() == SimulationContext.Application.RULE_BASED_STOCHASTIC; setBooleanAttribute(simulationcontext,XMLTags.RuleBasedAttrTag, ruleBased); @@ -1567,6 +1595,7 @@ public Element getXML(SimulationContext param, BioModel bioModel) throws XmlPars setBooleanAttribute(simulationcontext, XMLTags.RandomizeInitConditionTag,param.isRandomizeInitCondition()); } } + setBooleanAttribute(simulationcontext,XMLTags.MassConservationModelReductionTag, param.isUsingMassConservationModelReduction()); setBooleanAttribute(simulationcontext,XMLTags.InsufficientIterationsTag,param.isInsufficientIterations()); setBooleanAttribute(simulationcontext,XMLTags.InsufficientMaxMoleculesTag,param.isInsufficientMaxMolecules()); @@ -1821,7 +1850,26 @@ private Element getXML(FieldDataSymbol fds, ModelUnitSystem modelUnitSystem) { * @return Element * @param param cbit.vcell.mapping.SpeciesContextSpec */ -private Element getXML(SpeciesContextSpec param) { +/* + + + 0.0 + 10.0 + + + + + + + + + + + +*/ +private Element getXML(SpeciesContextSpec param, SimulationContext simContext) { Element speciesContextSpecElement = new Element(XMLTags.SpeciesContextSpecTag); //Add Attributes @@ -1852,11 +1900,64 @@ else if(initAmt != null) } //Add diffusion cbit.vcell.parser.Expression diffRate = param.getDiffusionParameter().getExpression(); - if (diffRate!=null){ - Element diffusion = new Element(XMLTags.DiffusionTag); - diffusion.addContent(mangleExpression(diffRate)); - speciesContextSpecElement.addContent(diffusion); + if (diffRate!=null) { + if(Application.SPRINGSALAD != simContext.getApplicationType()) { // in SS diffusion only happens at the site level + Element diffusion = new Element(XMLTags.DiffusionTag); + diffusion.addContent(mangleExpression(diffRate)); + speciesContextSpecElement.addContent(diffusion); + } + } + + // SpringSaLaD specific stuff + // the producer is dumb, we save whatever we have; the reader may be smart and check for consistency, maybe initialize what's missing with defaults? + if(Application.SPRINGSALAD == simContext.getApplicationType() && param.getInternalLinkSet() != null && param.getInternalLinkSet().size() > 0 ) { + for(MolecularInternalLinkSpec mils : param.getInternalLinkSet()) { + SpeciesContext sc = param.getSpeciesContext(); + SpeciesPattern sp = sc.getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns().size() != 1) { + break; // the species pattern must refer to exactly one molecule, links are intramollecular only + // throw new IllegalArgumentException("The species pattern must contain exactly one molecule."); + } + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); // the one and only + MolecularType mt = mtp.getMolecularType(); + + Element milsElement = new Element(XMLTags.InternalLinkSpecTag); + milsElement.setAttribute(XMLTags.MoleculeRefAttrTag, mt.getName()); + milsElement.setAttribute(XMLTags.SiteOneRefAttrTag, mils.getMolecularComponentPatternOne().getMolecularComponent().getName()); + milsElement.setAttribute(XMLTags.SiteTwoRefAttrTag, mils.getMolecularComponentPatternTwo().getMolecularComponent().getName()); + speciesContextSpecElement.addContent(milsElement); + } + } + if(Application.SPRINGSALAD == simContext.getApplicationType() && param.getSiteAttributesMap() != null && param.getSiteAttributesMap().size() > 0) { + for (Entry entry : param.getSiteAttributesMap().entrySet()) { + SpeciesContext sc = param.getSpeciesContext(); + SpeciesPattern sp = sc.getSpeciesPattern(); + if(sp == null || sp.getMolecularTypePatterns().size() != 1) { + break; // the species pattern must refer to exactly one molecule, links are intramollecular only + // throw new IllegalArgumentException("The species pattern must contain exactly one molecule."); + } + MolecularTypePattern mtp = sp.getMolecularTypePatterns().get(0); // the one and only + MolecularType mt = mtp.getMolecularType(); + + MolecularComponentPattern mcp = entry.getKey(); + SiteAttributesSpec sas = entry.getValue(); + Element sasElement = new Element(XMLTags.SiteAttributesSpecTag); + sasElement.setAttribute(XMLTags.SiteRefAttrTag, mcp.getMolecularComponent().getName()); + sasElement.setAttribute(XMLTags.MoleculeRefAttrTag, mt.getName()); + sasElement.setAttribute(XMLTags.SiteLocationRefAttrTag, sas.getLocation().getName()); + sasElement.setAttribute(XMLTags.SiteCoordXAttrTag, Double.toString(sas.getCoordinate().getX())); + sasElement.setAttribute(XMLTags.SiteCoordYAttrTag, Double.toString(sas.getCoordinate().getX())); + sasElement.setAttribute(XMLTags.SiteCoordZAttrTag, Double.toString(sas.getCoordinate().getX())); + sasElement.setAttribute(XMLTags.SiteRadiusAttrTag, Double.toString(sas.getRadius())); + sasElement.setAttribute(XMLTags.SiteDiffusionAttrTag, Double.toString(sas.getDiffusionRate())); + sasElement.setAttribute(XMLTags.SiteColorAttrTag, sas.getColor().getName()); + speciesContextSpecElement.addContent(sasElement); + } } + XMLOutputter outp = new XMLOutputter(Format.getPrettyFormat()); + String sout = outp.outputString(speciesContextSpecElement); + System.out.println(sout); + // write BoundaryConditions cbit.vcell.parser.Expression exp; Element boundaries = new Element(XMLTags.BoundariesTag); diff --git a/vcell-core/src/main/java/org/vcell/model/rbm/ComponentStatePattern.java b/vcell-core/src/main/java/org/vcell/model/rbm/ComponentStatePattern.java index b00802ab4b..64e9643cea 100644 --- a/vcell-core/src/main/java/org/vcell/model/rbm/ComponentStatePattern.java +++ b/vcell-core/src/main/java/org/vcell/model/rbm/ComponentStatePattern.java @@ -14,6 +14,7 @@ public class ComponentStatePattern extends RbmElementAbstract implements Matchab private final boolean bAny; public static final String strAny = "not specified"; + public static final String strNone = "none"; /* Example: diff --git a/vcell-core/src/main/java/org/vcell/model/rbm/MolecularComponentPattern.java b/vcell-core/src/main/java/org/vcell/model/rbm/MolecularComponentPattern.java index 5387ed5542..e4dc546826 100644 --- a/vcell-core/src/main/java/org/vcell/model/rbm/MolecularComponentPattern.java +++ b/vcell-core/src/main/java/org/vcell/model/rbm/MolecularComponentPattern.java @@ -1,13 +1,20 @@ package org.vcell.model.rbm; +import java.awt.Color; import java.util.List; import org.vcell.model.rbm.SpeciesPattern.Bond; import org.vcell.util.Compare; +import org.vcell.util.Coordinate; import org.vcell.util.Displayable; import org.vcell.util.Issue; import org.vcell.util.Issue.IssueCategory; import org.vcell.util.Issue.IssueSource; +import org.vcell.util.document.Identifiable; + +import cbit.vcell.model.Structure; +import cbit.vcell.parser.Expression; + import org.vcell.util.IssueContext; import org.vcell.util.Matchable; @@ -229,5 +236,4 @@ public String getDisplayName() { public String getDisplayType() { return typeName; } - } diff --git a/vcell-core/src/main/java/org/vcell/model/rbm/MolecularTypePattern.java b/vcell-core/src/main/java/org/vcell/model/rbm/MolecularTypePattern.java index 182ea0d5e0..4e9c16a9a9 100644 --- a/vcell-core/src/main/java/org/vcell/model/rbm/MolecularTypePattern.java +++ b/vcell-core/src/main/java/org/vcell/model/rbm/MolecularTypePattern.java @@ -5,8 +5,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.vcell.model.rbm.MolecularComponentPattern.BondType; import org.vcell.util.Compare; @@ -14,18 +16,23 @@ import org.vcell.util.Issue; import org.vcell.util.Issue.IssueCategory; import org.vcell.util.Issue.IssueSource; +import org.vcell.util.document.Identifiable; import org.vcell.util.IssueContext; import org.vcell.util.Matchable; +import cbit.vcell.mapping.SpeciesContextSpec; import cbit.vcell.model.Model; +import cbit.vcell.model.SpeciesContext; import cbit.vcell.model.Model.RbmModelContainer; public class MolecularTypePattern extends RbmElementAbstract implements Matchable, PropertyChangeListener, IssueSource, Displayable { + public static final String PROPERTY_NAME_COMPONENT_PATTERN_LIST = "componentPatternList"; public static final String TRIVIAL_MATCH = "*"; private MolecularType molecularType; private List componentPatternList = new ArrayList(); + private int index = 0; // purely for displaying purpose, since molecule can bind to itself private String participantMatchLabel = TRIVIAL_MATCH; // reactant-product match label, to avoid ambiguity private Map> processedMolecularComponentsMultiMap = new HashMap>(); diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java index 1873c18540..b0accdd7ec 100644 --- a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java +++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java @@ -2590,9 +2590,11 @@ public Map, String> getLocalToGlobalTranslationMap() { public static void validateSimulationContextSupport(SimulationContext simulationContext) throws UnsupportedSbmlExportException { String applicationTypeErrorMessage = null; - if (!simulationContext.getModel().getRbmModelContainer().isEmpty()) { + // relax the export restriction, we now may export fully flattened rule-based models (with no reaction rules) + // note that all the molecular type info will be lost; TODO: maybe, for later: save the bngl string as annotation where possible + if (!simulationContext.getModel().getRbmModelContainer().getReactionRuleList().isEmpty()) { applicationTypeErrorMessage = "Application '" + simulationContext.getName() + "' has reaction rules, SBML Export is not supported"; - }else { + } else { switch (simulationContext.getApplicationType()) { case NETWORK_DETERMINISTIC: { break; diff --git a/vcell-core/src/main/java/org/vcell/sedml/ModelFormat.java b/vcell-core/src/main/java/org/vcell/sedml/ModelFormat.java index c773915561..d31a940f2e 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/ModelFormat.java +++ b/vcell-core/src/main/java/org/vcell/sedml/ModelFormat.java @@ -1,5 +1,5 @@ package org.vcell.sedml; public enum ModelFormat { - SBML, VCML; + SBML, VCML, SPRINGSALAD; } diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java index 755ee027d3..e25b734f66 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java @@ -193,7 +193,10 @@ public List getBioModels() { for(SolverDescription.SolverFeature sf : sfList) { switch(sf) { case Feature_Rulebased: - appType = Application.RULE_BASED_STOCHASTIC; + if(appType != Application.SPRINGSALAD) { + // springs(alad) type takes precedence + appType = Application.RULE_BASED_STOCHASTIC; + } break; case Feature_Stochastic: appType = Application.NETWORK_STOCHASTIC; @@ -201,6 +204,9 @@ public List getBioModels() { case Feature_Deterministic: appType = Application.NETWORK_DETERMINISTIC; break; + case Feature_Springs: + appType = Application.SPRINGSALAD; + break; case Feature_Spatial: bSpatial = true; break; diff --git a/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinFileWriter.java b/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinFileWriter.java new file mode 100644 index 0000000000..4f1377ec2a --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinFileWriter.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package org.vcell.solver.langevin; + +import java.io.PrintWriter; + +import org.apache.commons.io.output.WriterOutputStream; +import org.jdom.Element; + +import cbit.util.xml.XmlUtil; +import cbit.vcell.messaging.server.SimulationTask; +import cbit.vcell.solver.server.SolverFileWriter; + +import java.io.File; +import java.io.FileFilter; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.StringTokenizer; + +import org.apache.commons.io.FileUtils; +import org.vcell.util.document.KeyValue; +import org.vcell.util.document.User; +import org.vcell.util.document.VCDataIdentifier; + +import cbit.vcell.simdata.DataSetControllerImpl; +import cbit.vcell.simdata.ODEDataBlock; +import cbit.vcell.simdata.SimulationData; +import cbit.vcell.solver.LangevinSimulationOptions; +import cbit.vcell.solver.VCSimulationDataIdentifier; +import cbit.vcell.solver.VCSimulationIdentifier; +import cbit.vcell.solver.ode.ODESimData; +import cbit.vcell.util.ColumnDescription; + +/** + * The function reads model information from simulation and + * generates the stochastic input file for simulation engine. + */ +public class LangevinFileWriter extends SolverFileWriter +{ + private long randomSeed = 0; //value assigned in the constructor + +public LangevinFileWriter(PrintWriter pw, SimulationTask simTask, boolean bMessaging) +{ + super(pw, simTask, bMessaging); +} + +@Override +public void write(String[] parameterNames) throws Exception { + LangevinSimulationOptions langevinSimulationOptions = simTask.getSimulation().getSolverTaskDescription().getLangevinSimulationOptions(); + String langevinLngvString = LangevinLngvWriter.writeLangevinLngv(simTask.getSimulation(), randomSeed, langevinSimulationOptions); + + printWriter.write(langevinLngvString); + printWriter.flush(); +} + +public static void main(String[] args) { + try { + + System.out.println("Done"); + } catch (Exception e) { + e.printStackTrace(); + } +} + + +} diff --git a/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinLngvWriter.java b/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinLngvWriter.java new file mode 100644 index 0000000000..d069330fa5 --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinLngvWriter.java @@ -0,0 +1,957 @@ +package org.vcell.solver.langevin; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.vcell.util.Pair; + +import cbit.vcell.geometry.Geometry; +import cbit.vcell.geometry.GeometrySpec; +import cbit.vcell.mapping.GeometryContext; +import cbit.vcell.mapping.ReactionRuleSpec; +import cbit.vcell.mapping.ReactionRuleSpec.Subtype; +import cbit.vcell.mapping.ReactionRuleSpec.TransitionCondition; +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SpeciesContextSpec; +import cbit.vcell.mapping.SimulationContext.Application; +import cbit.vcell.math.Action; +import cbit.vcell.math.JumpProcessRateDefinition; +import cbit.vcell.math.LangevinParticleJumpProcess; +import cbit.vcell.math.LangevinParticleMolecularComponent; +import cbit.vcell.math.LangevinParticleMolecularType; +import cbit.vcell.math.MacroscopicRateConstant; +import cbit.vcell.math.MathDescription; +import cbit.vcell.math.MathException; +import cbit.vcell.math.MathUtilities; +import cbit.vcell.math.ParticleComponentStateDefinition; +import cbit.vcell.math.ParticleComponentStatePattern; +import cbit.vcell.math.ParticleJumpProcess; +import cbit.vcell.math.ParticleMolecularComponent; +import cbit.vcell.math.ParticleMolecularComponentPattern; +import cbit.vcell.math.ParticleMolecularType; +import cbit.vcell.math.ParticleMolecularTypePattern; +import cbit.vcell.math.ParticleProperties; +import cbit.vcell.math.ParticleProperties.ParticleInitialCondition; +import cbit.vcell.math.ParticleProperties.ParticleInitialConditionCount; +import cbit.vcell.math.ParticleSpeciesPattern; +import cbit.vcell.math.SubDomain; +import cbit.vcell.math.Variable; +import cbit.vcell.mathmodel.MathModel; +import cbit.vcell.messaging.server.SimulationTask; +import cbit.vcell.parser.DivideByZeroException; +import cbit.vcell.parser.Expression; +import cbit.vcell.parser.ExpressionException; +import cbit.vcell.solver.LangevinSimulationOptions; +import cbit.vcell.solver.Simulation; +import cbit.vcell.solver.SimulationOwner; +import cbit.vcell.solver.SimulationSymbolTable; +import cbit.vcell.solver.SolverException; +//import org.jdom.output.Format; + +public class LangevinLngvWriter { + + public static final boolean TrackClusters = true; // SpringSaLaD specific + public static final boolean InitialLocationRandom = true; + private static final String InitialLocationRandomString = "Random"; + private static final String InitialLocationSetString = "Set"; + public static final String SourceMoleculeString = "Source"; // molecule used in creation reaction subtype (reserved name) + public static final String SinkMoleculeString = "Sink"; // molecule used in decay reaction subtype (reserved name) + private static final String AnchorSiteString = "Anchor"; // required name for reserved special Site of membrane species + private static final String AnchorStateString = "Anchor"; // required name for reserved special State of the Anchor site + + public final static String SPATIAL_INFORMATION = "SYSTEM INFORMATION"; + public final static String TIME_INFORMATION = "TIME INFORMATION"; + public final static String MOLECULES = "MOLECULES"; + public final static String MOLECULE_FILES = "MOLECULE FILES"; + public final static String DECAY_REACTIONS = "CREATION/DECAY REACTIONS"; + public final static String TRANSITION_REACTIONS = "STATE TRANSITION REACTIONS"; + public final static String ALLOSTERIC_REACTIONS = "ALLOSTERIC REACTIONS"; + public final static String BINDING_REACTIONS = "BIMOLECULAR BINDING REACTIONS"; + public final static String MOLECULE_COUNTERS = "MOLECULE COUNTERS"; + public final static String STATE_COUNTERS = "STATE COUNTERS"; + public final static String BOND_COUNTERS = "BOND COUNTERS"; + public final static String SITE_PROPERTY_COUNTERS = "SITE PROPERTY COUNTERS"; + public final static String CLUSTER_COUNTERS = "CLUSTER COUNTERS"; + public final static String SYSTEM_ANNOTATION = "SYSTEM ANNOTATIONS"; + public final static String MOLECULE_ANNOTATIONS = "MOLECULE ANNOTATIONS"; + public final static String REACTION_ANNOTATIONS = "REACTION ANNOTATIONS"; + + public final static String DEFAULT_FOLDER = "Default Folder"; + private String systemName; // The system name (same as the file name, usually) + public final static String DEFAULT_SYSTEM_NAME = "New Model"; + + // various collections here for the intermediate stuff as we build the lngv file from math + private static Map particlePropertiesMap = new LinkedHashMap<> (); // initial conditions for seed species + private static Map particleJumpProcessMap = new LinkedHashMap<> (); // list of reactions + private static Set particleMolecularTytpeSet = new LinkedHashSet<> (); // molecular types + + private static GeometryContext geometryContext = null; + private static MathDescription mathDescription = null; + +// static ArrayList currentMappingOfReactionParticipants = new ArrayList(); +// static HashSet reactionReactantBondSites = new HashSet(); + + // main work being done here + public static String writeLangevinLngv(Simulation simulation, long randomSeed, LangevinSimulationOptions langevinSimulationOptions) throws SolverException, DivideByZeroException, ExpressionException { + try { + System.out.println("VCML ORIGINAL .... START\n"+simulation.getMathDescription().getVCML_database()+"\nVCML ORIGINAL .... END\n====================\n"); + } catch (MathException e1) { + e1.printStackTrace(); + } + + SimulationOwner so = simulation.getSimulationOwner(); + if(so instanceof MathModel) { + MathModel mm = (MathModel)so; // TODO: must make this code compatible to math model too + // TODO: how do I get GeometryContext for a math model? + } + Geometry geometry = so.getGeometry(); + GeometrySpec geometrySpec = geometry.getGeometrySpec(); + if(!(so instanceof SimulationContext)) { + throw new RuntimeException("SimulationOwner must be instance of SimulationContext"); + } + SimulationContext simContext = (SimulationContext)so; + geometryContext = simContext.getGeometryContext(); + + if(simContext.getApplicationType() != Application.SPRINGSALAD) { + throw new RuntimeException("SpringSaLaD application type expected."); + } + if(simContext.getMostRecentlyCreatedMathMapping() == null) { + throw new RuntimeException("Refresh MathDescription required"); + } + +// LangevinMathMapping mathMapping = (LangevinMathMapping)simContext.getMostRecentlyCreatedMathMapping(); +// Structure struct = null; +// StructureMapping sm = simContext.getGeometryContext().getStructureMapping(struct); +// GeometryClass reactionStepGeometryClass = sm.getGeometryClass(); + + mathDescription = simulation.getMathDescription(); + particlePropertiesMap.clear(); + particleJumpProcessMap.clear(); + particleMolecularTytpeSet.clear(); + + Enumeration subDomainEnum = mathDescription.getSubDomains(); + while (subDomainEnum.hasMoreElements()) { + SubDomain subDomain = subDomainEnum.nextElement(); + for(ParticleProperties pp : subDomain.getParticleProperties()) { + particlePropertiesMap.put(pp, subDomain); + } + + for(ParticleJumpProcess pjp : subDomain.getParticleJumpProcesses()) { + if(!(pjp instanceof LangevinParticleJumpProcess)) { + throw new RuntimeException("LangevinParticleJumpProcess expected."); + } + LangevinParticleJumpProcess lpjp = (LangevinParticleJumpProcess)pjp; + particleJumpProcessMap.put(lpjp, subDomain); + } + } + + for(ParticleMolecularType pmt : mathDescription.getParticleMolecularTypes()) { + if(!(pmt instanceof LangevinParticleMolecularType)) { + throw new RuntimeException("LangevinParticleMolecularType expected."); + } + LangevinParticleMolecularType lpmt = (LangevinParticleMolecularType)pmt; + particleMolecularTytpeSet.add(lpmt); + } + + StringBuilder sb = new StringBuilder(); + /* ********* BEGIN BY WRITING THE TIMES *********/ + sb.append("*** " + TIME_INFORMATION + " ***"); + sb.append("\n"); + writeTimeInformation(sb, simulation); + sb.append("\n"); + + /* ********* WRITE THE SPATIAL INFORMATION **********/ + sb.append("*** " + SPATIAL_INFORMATION + " ***"); + sb.append("\n"); + geometryContext.writeData(sb); + sb.append("\n"); + + /* ******* WRITE THE SPECIES INFORMATION ***********/ + sb.append("*** " + MOLECULES + " ***"); + sb.append("\n"); + sb.append("\n"); + writeSpeciesInfo(sb); + + /* ******* WRITE THE MOLECULE INFORMATION FILES ***********/ + sb.append("*** " + MOLECULE_FILES + " ***"); + sb.append("\n"); + sb.append("\n"); + writeSpeciesFileInfo(sb); + sb.append("\n"); + + /* ******* WRITE THE CREATION / DECAY REACTIONS ***************/ + sb.append("*** " + DECAY_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + writeCreationDecayReactions(sb); + sb.append("\n"); + + /* ******* WRITE THE TRANSITION REACTIONS **********/ + sb.append("*** " + TRANSITION_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + writeTransitionReactions(sb); + sb.append("\n"); + + /* ******* WRITE THE ALLOSTERIC REACTIONS **********/ + sb.append("*** " + ALLOSTERIC_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + writeAllostericReactions(sb); + sb.append("\n"); + + /* ******* WRITE THE BINDING REACTIONS ************/ + sb.append("*** " + BINDING_REACTIONS + " ***"); + sb.append("\n"); + sb.append("\n"); + writeBindingReactions(sb); + sb.append("\n"); + + /* ****** WRITE THE MOLECULE COUNTERS **********/ + sb.append("*** " + MOLECULE_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(Molecule molecule: molecules) { +// molecule.getMoleculeCounter().writeMoleculeCounter(sb); +// } + Simulation.Counters.writeMoleculeCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* ****** WRITE THE STATE COUNTERS *************/ + sb.append("*** " + STATE_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(Molecule molecule : molecules) { +// for(SiteType type : molecule.getTypeArray()) { +// for(State state : type.getStates()) { +// state.getStateCounter().writeStateCounter(sb); +// } +// } +// } + Simulation.Counters.writeStateCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + // what follows is mainly placeholders for result statistics, obviously there's none before running the simulation + /* ***** WRITE THE BOND COUNTERS ***************/ + sb.append("*** " + BOND_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(BindingReaction reaction: bindingReactions) { +// reaction.getBondCounter().writeBondCounter(sb); +// } + Simulation.Counters.writeBondCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* ******** WRITE THE SITE PROPERTY COUNTERS ************/ + sb.append("*** " + SITE_PROPERTY_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); +// for(Molecule molecule : molecules) { +// ArrayList sites = molecule.getSiteArray(); +// for(Site site : sites) { +// site.getPropertyCounter().writeSitePropertyCounter(sb); +// } +// } + Simulation.Counters.writeSitePropertyCounters(simContext, sb); // everything here is initialized with default + sb.append("\n"); + + /* *************** WRITE THE TRACK CLUSTERS BOOLEAN ***********/ + sb.append("*** " + CLUSTER_COUNTERS + " ***"); + sb.append("\n"); + sb.append("\n"); + sb.append("Track_Clusters: " + SpeciesContextSpec.TrackClusters); + sb.append("\n"); + sb.append("\n"); + + // TODO: I'm not sure what's the point of using annotations here + /* ****** WRITE THE SYSTEM ANNOTATION ********************/ + sb.append("*** " + SYSTEM_ANNOTATION + " ***"); + sb.append("\n"); + sb.append("\n"); +// systemAnnotation.printAnnotation(sb); + sb.append("\n"); + + /* ****** WRITE THE MOLECULE ANNOTATIONS *****************/ + sb.append("*** " + MOLECULE_ANNOTATIONS + " ***"); + sb.append("\n"); + sb.append("\n"); +// writeMoleculeAnnotations(sb); + sb.append("\n"); + + /* ****** WRITE THE REACTION ANNOTATIONS *****************/ + sb.append("*** " + REACTION_ANNOTATIONS + " ***"); + sb.append("\n"); + sb.append("\n"); +// writeReactionAnnotations(sb); + sb.append("\n"); + + String ret = sb.toString(); + System.out.println(ret); + + return ret; + } + + private static void writeTimeInformation(StringBuilder sb, Simulation simulation) { + // general stuff is in solver task description + simulation.getSolverTaskDescription().writeData(sb); + + // for fast simulation for a simple transition state model, select the following time simulation options: + // - ending: 0.01 (langevin: total time) + // - time step (default): 1E-7 (langevin: dt) + // - output interval: 1E-4 (langevin: dt_data) + LangevinSimulationOptions lso = simulation.getSolverTaskDescription().getLangevinSimulationOptions(); + sb.append("dt_spring: " + lso.getIntervalSpring()); // 1.00E-9 default + sb.append("\n"); + sb.append("dt_image: " + lso.getIntervalImage()); // 1.00E-4 default + sb.append("\n"); + + } + + private static void writeBindingReactions(StringBuilder sb) { + Map nameToProcessDirect = new LinkedHashMap<> (); + Map nameToProcessReverse = new LinkedHashMap<> (); // need this only for reverse rate + for(Map.Entry entry : particleJumpProcessMap.entrySet()) { + LangevinParticleJumpProcess lpjp = entry.getKey(); + Subtype subtype = lpjp.getSubtype(); + if(Subtype.BINDING == subtype) { + if(!lpjp.getName().endsWith("_reverse")) { + String lpjpName = lpjp.getName(); + nameToProcessDirect.put(lpjpName, lpjp); + } else { + String lpjpName = lpjp.getName().substring(0, lpjp.getName().lastIndexOf("_reverse")); + nameToProcessReverse.put(lpjpName, lpjp); + } + } + } + + for(Map.Entry entry : nameToProcessDirect.entrySet()) { + Map pmtpReactantBondMap = new LinkedHashMap<> (); + String name = entry.getKey(); + LangevinParticleJumpProcess lpjpDirect = entry.getValue(); + + for(Action action : lpjpDirect.getActions()) { + String operation = action.getOperation(); + if(Action.ACTION_CREATE.equals(operation)) { + ; // don't need any product info + } else if(Action.ACTION_DESTROY.equals(operation)) { + Variable var = action.getVar(); + if(!(var instanceof ParticleSpeciesPattern)) { + throw new RuntimeException("Binding reaction reactant variable must be ParticleSpeciesPattern"); + } + ParticleSpeciesPattern pspReactant = (ParticleSpeciesPattern)var; + if(pspReactant.getParticleMolecularTypePatterns().size() != 1) { + throw new RuntimeException("Each reactant of a binding reaction must have exactly 1 molecule"); + } + // we don't know yet which site will bind, so we initialize with null + pmtpReactantBondMap.put(pspReactant.getParticleMolecularTypePatterns().get(0), null); + } + } + if(pmtpReactantBondMap.size() != 2) { + throw new RuntimeException("A binding reaction must have exactly 2 single molecule participants"); + } + + // we really only care about the reactants, each must have one single unbound site + // which is the one that will create the bond with the other reactant's unbound site + for(Map.Entry entry1 : pmtpReactantBondMap.entrySet()) { + ParticleMolecularTypePattern pmtp = entry1.getKey(); + int numNonTrivialBonds = 0; + for(ParticleMolecularComponentPattern pmcp : pmtp.getMolecularComponentPatternList()) { + if(ParticleMolecularComponentPattern.ParticleBondType.Possible != pmcp.getBondType()) { + if(ParticleMolecularComponentPattern.ParticleBondType.None != pmcp.getBondType()) { + throw new RuntimeException("Each reactant of a binding reaction cannot have any 'Exists' or 'Specified' bond"); + } + pmtpReactantBondMap.put(pmtp, pmcp); + numNonTrivialBonds++; + } + } + if(numNonTrivialBonds != 1) { + throw new RuntimeException("Each reactant of a binding reaction must have exactly one non-trivial bond"); + } + } + + // calculate onRate as string, from lpjpDirect + String onRate = null; + Expression kOn = null; + MacroscopicRateConstant mrc = null; + JumpProcessRateDefinition particleRateDefinition = lpjpDirect.getParticleRateDefinition(); + if(particleRateDefinition instanceof MacroscopicRateConstant) { + mrc = (MacroscopicRateConstant)particleRateDefinition; + } else { + throw new RuntimeException("Rate definition must be MacroscopicRateConstant"); + } + try { + kOn = mrc.getExpression(); + } catch(Exception e) { + throw new RuntimeException("Reaction Rate expression is wrong"); + } + Expression exp = null; + try { + exp = MathUtilities.substituteFunctions(kOn, mathDescription, false); + } catch (ExpressionException e) { + throw new RuntimeException("kOn substitution failed, " + e.getMessage()); + } + try { + double rate = exp.evaluateConstant(); + onRate = Double.toString(rate); + } catch (Exception e) { + throw new RuntimeException("rate must be a number"); + } + + // calculate offRate as string, from lpjpReverse + String offRate = null; + LangevinParticleJumpProcess lpjpReverse = nameToProcessReverse.get(name); + Expression kOff = null; + mrc = null; + particleRateDefinition = lpjpReverse.getParticleRateDefinition(); + if(particleRateDefinition instanceof MacroscopicRateConstant) { + mrc = (MacroscopicRateConstant)particleRateDefinition; + } else { + throw new RuntimeException("Rate definition must be MacroscopicRateConstant"); + } + try { + kOff = mrc.getExpression(); + } catch(Exception e) { + throw new RuntimeException("Reaction Rate expression is wrong"); + } + exp = null; + try { + exp = MathUtilities.substituteFunctions(kOff, mathDescription, false); + } catch (ExpressionException e) { + throw new RuntimeException("kOff substitution failed, " + e.getMessage()); + } + try { + double rate = exp.evaluateConstant(); + offRate = Double.toString(rate); + } catch (Exception e) { + throw new RuntimeException("rate must be a number"); + } + + Set> mapSet = pmtpReactantBondMap.entrySet(); + Map.Entry elementOne = + (new ArrayList>(mapSet)).get(0); + Map.Entry elementTwo = + (new ArrayList>(mapSet)).get(1); + + // finally write the BINDING_REACTIONS block for each binding reaction + sb.append("'").append(lpjpDirect.getName()).append("' "); + sb.append("'").append(elementOne.getKey().getMolecularType().getName()).append("' : '") + .append(elementOne.getValue().getMolecularComponent().getName()).append("' : '") + .append(elementOne.getValue().getComponentStatePattern().getParticleComponentStateDefinition().getName()); + sb.append("' + '"); + sb.append(elementTwo.getKey().getMolecularType().getName()).append("' : '") + .append(elementTwo.getValue().getMolecularComponent().getName()).append("' : '") + .append(elementTwo.getValue().getComponentStatePattern().getParticleComponentStateDefinition().getName()); + + sb.append("' kon ").append(onRate); + sb.append(" koff ").append(offRate); + sb.append(" Bond_Length ").append(Double.toString(lpjpDirect.getBondLength())); + sb.append("\n"); + } + String ret = sb.toString(); + System.out.println(ret); + return; + } + + private static void writeAllostericReactions(StringBuilder sb) { + for(Map.Entry entry : particleJumpProcessMap.entrySet()) { + LangevinParticleJumpProcess lpjp = entry.getKey(); + Subtype subtype = lpjp.getSubtype(); + if(Subtype.ALLOSTERIC == subtype) { + ParticleSpeciesPattern pspReactant = null; + ParticleSpeciesPattern pspProduct = null; + for(Action action : lpjp.getActions()) { + String operation = action.getOperation(); + if(Action.ACTION_CREATE.equals(operation)) { + Variable var =action.getVar(); + if(!(var instanceof ParticleSpeciesPattern)) { + throw new RuntimeException("Allosteric product variable must be ParticleSpeciesPattern"); + } + pspProduct = (ParticleSpeciesPattern)var; + } else if(Action.ACTION_DESTROY.equals(operation)) { + Variable var = action.getVar(); + if(!(var instanceof ParticleSpeciesPattern)) { + throw new RuntimeException("Allosteric reactant variable must be ParticleSpeciesPattern"); + } + pspReactant = (ParticleSpeciesPattern)var; + } + } + + // calculate onRate as string + Expression kOn = null; + MacroscopicRateConstant mrc = null; + JumpProcessRateDefinition particleRateDefinition = lpjp.getParticleRateDefinition(); + if(particleRateDefinition instanceof MacroscopicRateConstant) { + mrc = (MacroscopicRateConstant)particleRateDefinition; + } else { + throw new RuntimeException("Rate definition must be MacroscopicRateConstant"); + } + try { + kOn = mrc.getExpression(); + } catch(Exception e) { + throw new RuntimeException("Reaction Rate expression is wrong"); + } + Expression exp = null; + String onRate = null; + try { + exp = MathUtilities.substituteFunctions(kOn, mathDescription, false); + } catch (ExpressionException e) { + throw new RuntimeException("kOn substitution failed, " + e.getMessage()); + } + try { + double rate = exp.evaluateConstant(); + onRate = Double.toString(rate); + } catch (Exception e) { + throw new RuntimeException("rate must be a number"); + } + + // allosteric is a type of conditional transition + ParticleMolecularTypePattern pmtpReactant = null; // allosteric reactant molecule + ParticleMolecularTypePattern pmtpProduct = null; + ParticleMolecularComponentPattern pmcpTransitionReactant = null; // transition reactant site + ParticleComponentStateDefinition pcsdTransitionReactant = null; // begin transition state + ParticleComponentStateDefinition pcsdTransitionProduct = null; // end transition state + ParticleMolecularComponentPattern pmcpConditionReactant = null; // condition reactant site + ParticleComponentStateDefinition pcsdConditionReactant = null; // condition state + + if(pspReactant.getParticleMolecularTypePatterns().size() == 1) { + pmtpReactant = pspReactant.getParticleMolecularTypePatterns().get(0); + pmtpProduct = pspProduct.getParticleMolecularTypePatterns().get(0); + } else { + throw new RuntimeException("Bound conditional transition reactant size must be 2"); + } + + // now identify the alosteric condition and transition + for(ParticleMolecularComponentPattern pmcpReactant : pmtpReactant.getMolecularComponentPatternList()) { + ParticleComponentStatePattern pcspReactant = pmcpReactant.getComponentStatePattern(); + if(pcspReactant.isAny()) { + continue; + } + ParticleComponentStateDefinition pcsdReactant = pcspReactant.getParticleComponentStateDefinition(); + ParticleMolecularComponentPattern pmcpProduct = pmtpProduct.getMolecularComponentPattern(pmcpReactant.getMolecularComponent()); + ParticleComponentStateDefinition pcsdProduct = pmcpProduct.getComponentStatePattern().getParticleComponentStateDefinition(); + if(pcsdReactant.getName().equals(pcsdProduct.getName())) { // allosteric condition + pmcpConditionReactant = pmcpReactant; + pcsdConditionReactant = pcsdReactant; + } else { // allosteric transition + pmcpTransitionReactant = pmcpReactant; + pcsdTransitionReactant = pcsdReactant; + pcsdTransitionProduct = pcsdProduct; + } + } + + int transitionIndex = pmtpReactant.getMolecularType().getComponentList().indexOf(pmcpTransitionReactant.getMolecularComponent()); + int conditionIndex = pmtpReactant.getMolecularType().getComponentList().indexOf(pmcpConditionReactant.getMolecularComponent()); + // finally build the transition block + sb.append("'").append(lpjp.getName()).append("' :: "); + sb.append("'").append(pmtpReactant.getMolecularType().getName()).append("' : ") + .append("Site ").append(transitionIndex).append(" : '") + .append(pcsdTransitionReactant.getName()); + sb.append("' --> '"); + sb.append(pcsdTransitionProduct.getName()); + sb.append("' Rate ").append(onRate); + sb.append(" Allosteric_Site ").append(conditionIndex); + sb.append(" State '").append(pcsdConditionReactant.getName()).append("'"); + sb.append("\n"); + } + } + String ret = sb.toString(); + System.out.println(ret); + return; + } + + + private static void writeTransitionReactions(StringBuilder sb) { + for(Map.Entry entry : particleJumpProcessMap.entrySet()) { + LangevinParticleJumpProcess lpjp = entry.getKey(); +// SubDomain subDomain = entry.getValue(); + Subtype subtype = lpjp.getSubtype(); + if(Subtype.TRANSITION == subtype) { + ParticleSpeciesPattern pspReactant = null; + ParticleSpeciesPattern pspProduct = null; + for(Action action : lpjp.getActions()) { + String operation = action.getOperation(); + if(Action.ACTION_CREATE.equals(operation)) { + Variable var =action.getVar(); + if(!(var instanceof ParticleSpeciesPattern)) { + throw new RuntimeException("Transition product variable must be ParticleSpeciesPattern"); + } + pspProduct = (ParticleSpeciesPattern)var; + } else if(Action.ACTION_DESTROY.equals(operation)) { + Variable var = action.getVar(); + if(!(var instanceof ParticleSpeciesPattern)) { + throw new RuntimeException("Transition reactant variable must be ParticleSpeciesPattern"); + } + pspReactant = (ParticleSpeciesPattern)var; + } + } + + Expression kOn = null; + MacroscopicRateConstant mrc = null; + JumpProcessRateDefinition particleRateDefinition = lpjp.getParticleRateDefinition(); + if(particleRateDefinition instanceof MacroscopicRateConstant) { + mrc = (MacroscopicRateConstant)particleRateDefinition; + } else { + throw new RuntimeException("Rate definition must be MacroscopicRateConstant"); + } + try { + kOn = mrc.getExpression(); + } catch(Exception e) { + throw new RuntimeException("Reaction Rate expression is wrong"); + } + Expression exp = null; + String onRate = null; + try { + exp = MathUtilities.substituteFunctions(kOn, mathDescription, false); + } catch (ExpressionException e) { + throw new RuntimeException("kOn substitution failed, " + e.getMessage()); + } + try { + double rate = exp.evaluateConstant(); + onRate = Double.toString(rate); + } catch (Exception e) { + throw new RuntimeException("rate must be a number"); + } + + ParticleMolecularTypePattern pmtpTransitionReactant = null; // transition reactant molecule + ParticleMolecularTypePattern pmtpTransitionProduct = null; + ParticleMolecularTypePattern pmtpConditionReactant = null; // condition reactant molecule + ParticleMolecularComponentPattern pmcpTransitionReactant = null; // transition reactant site + ParticleComponentStateDefinition pcsdTransitionReactant = null; // begin transition state + ParticleComponentStateDefinition pcsdTransitionProduct = null; // end transition state + ParticleMolecularComponentPattern pmcpConditionReactant = null; // condition reactant site + ParticleComponentStateDefinition pcsdConditionReactant = null; // condition state + + TransitionCondition transitionCondition = lpjp.getTransitionCondition(); + if(TransitionCondition.BOUND == transitionCondition) { + if(pspReactant.getParticleMolecularTypePatterns().size() == 1) { + // illegal, bound transitions must have separate condition and transition molecules by convention + throw new RuntimeException("Bound conditional transition reactant size must be 2"); + } else if(pspReactant.getParticleMolecularTypePatterns().size() == 2) { + ParticleMolecularTypePattern pmtpReactant0 = pspReactant.getParticleMolecularTypePatterns().get(0); + ParticleMolecularTypePattern pmtpProduct0 = pspProduct.getParticleMolecularTypePatterns().get(0); + ParticleMolecularTypePattern pmtpReactant1 = pspReactant.getParticleMolecularTypePatterns().get(1); + ParticleMolecularTypePattern pmtpProduct1 = pspProduct.getParticleMolecularTypePatterns().get(1); + if(pmtpReactant0.getMolecularType() != pmtpProduct0.getMolecularType()) { + // if the order of molecules in the reactant and product are inverted, match them properly + pmtpProduct0 = pspProduct.getParticleMolecularTypePatterns().get(1); + pmtpProduct1 = pspProduct.getParticleMolecularTypePatterns().get(0); + } + if(pmtpReactant0.compareEqual(pmtpProduct0)) { + pmtpTransitionReactant = pmtpReactant1; // transition reactant molecule + pmtpTransitionProduct = pmtpProduct1; // transition product molecule + pmtpConditionReactant = pmtpReactant0; // this is the condition molecule + } else if(pmtpReactant1.compareEqual(pmtpProduct1)) { + pmtpTransitionReactant = pmtpReactant0; // transition reactant molecule + pmtpTransitionProduct = pmtpProduct0; // transition product molecule + pmtpConditionReactant = pmtpReactant1; // this is the condition molecule + } else { + throw new RuntimeException("No matching condition found for bound conditional transition"); + } + } else { + throw new RuntimeException("Bound conditional transition reactant size must be 2"); + } + // identify the binding condition site and state + for(ParticleMolecularComponentPattern pmcp : pmtpConditionReactant.getMolecularComponentPatternList()) { + if(pmcp.getComponentStatePattern().isAny()) { + continue; + } + ParticleComponentStatePattern pcsp = pmcp.getComponentStatePattern(); + pmcpConditionReactant = pmcp; + pcsdConditionReactant = pcsp.getParticleComponentStateDefinition(); + break; // found the one and only condition + } + } else if(TransitionCondition.NONE == transitionCondition) { + pmtpTransitionReactant = pspReactant.getParticleMolecularTypePatterns().get(0); + pmtpTransitionProduct = pspProduct.getParticleMolecularTypePatterns().get(0); + } else if(TransitionCondition.FREE == transitionCondition) { + pmtpTransitionReactant = pspReactant.getParticleMolecularTypePatterns().get(0); + pmtpTransitionProduct = pspProduct.getParticleMolecularTypePatterns().get(0); + } else { + throw new RuntimeException("Unexpected transition condition"); + } + + // identify the transition site and the starting and ending state + for(ParticleMolecularComponentPattern pmcp : pmtpTransitionReactant.getMolecularComponentPatternList()) { + if(pmcp.getComponentStatePattern().isAny()) { + continue; + } + ParticleComponentStatePattern pcsp = pmcp.getComponentStatePattern(); + pmcpTransitionReactant = pmcp; + pcsdTransitionReactant = pcsp.getParticleComponentStateDefinition(); + break; // found the one and only transition + } + for(ParticleMolecularComponentPattern pmcp : pmtpTransitionProduct.getMolecularComponentPatternList()) { + if(pmcp.getComponentStatePattern().isAny()) { + continue; + } + ParticleComponentStatePattern pcsp = pmcp.getComponentStatePattern(); + pcsdTransitionProduct = pcsp.getParticleComponentStateDefinition(); + break; // found the one and only transition + } + + // finally build the transition block + sb.append("'").append(lpjp.getName()).append("' :: "); + sb.append("'").append(pmtpTransitionReactant.getMolecularType().getName()).append("' : '") + .append(pmcpTransitionReactant.getMolecularComponent().getName()).append("' : '") + .append(pcsdTransitionReactant.getName()); + sb.append("' --> '"); + sb.append(pcsdTransitionProduct.getName()); + sb.append("' Rate ").append(onRate); + sb.append(" Condition ").append(transitionCondition.lngvName); + if(TransitionCondition.BOUND == transitionCondition) { + sb.append(" '").append(pmtpConditionReactant.getMolecularType().getName()).append("' : '") + .append(pmcpConditionReactant.getMolecularComponent().getName()).append("' : '") + .append(pcsdConditionReactant); + } + sb.append("\n"); + } // end if Subtype.TRANSITION + } + String ret = sb.toString(); + System.out.println(ret); + return; + } + + private static void writeCreationDecayReactions(StringBuilder sb) { + Map> creationDecayVariableMap = new LinkedHashMap<> (); + for( Map.Entry entry : particlePropertiesMap.entrySet()) { + ParticleProperties pp = entry.getKey(); + Variable var = pp.getVariable(); + Pair rates = new Pair<> ("0", "0"); + creationDecayVariableMap.put(var, rates); + } + for(Map.Entry entry : particleJumpProcessMap.entrySet()) { + LangevinParticleJumpProcess lpjp = entry.getKey(); + Subtype subtype = lpjp.getSubtype(); + if(Subtype.CREATION == subtype) { + for(Action action : lpjp.getActions()) { + if(Action.ACTION_CREATE.equals(action.getOperation())) { // species being created + Pair rates = null; + Variable var = action.getVar(); + MacroscopicRateConstant mrc = null; + JumpProcessRateDefinition particleRateDefinition = lpjp.getParticleRateDefinition(); + if(particleRateDefinition instanceof MacroscopicRateConstant) { + mrc = (MacroscopicRateConstant)particleRateDefinition; + } else { + throw new RuntimeException("Rate definition must be MacroscopicRateConstant"); + } + Expression kCreate = null; + try { + kCreate = mrc.getExpression(); + } catch(Exception e) { + throw new RuntimeException("Reaction Rate expression is wrong"); + } + if(creationDecayVariableMap.containsKey(var)) { + rates = creationDecayVariableMap.get(var); + if(!rates.one.equals("0")) { + throw new RuntimeException("Cannot have multiple creation reactions for the same Variable"); + } + Expression exp = null; + String creationRate = null; + try { + exp = MathUtilities.substituteFunctions(kCreate, mathDescription, false); + } catch (ExpressionException e) { + throw new RuntimeException("kCreate substitution failed, " + e.getMessage()); + } + try { + double rate = exp.evaluateConstant(); + creationRate = Double.toString(rate); + } catch (Exception e) { + throw new RuntimeException("rate must be a number"); + } + rates = new Pair<> (creationRate, rates.two); + } else { + throw new RuntimeException("Variable missing in the variables map"); + } + creationDecayVariableMap.put(var, rates); + } + } + } else if(Subtype.DECAY == subtype) { + for(Action action : lpjp.getActions()) { + if(Action.ACTION_DESTROY.equals(action.getOperation())) { // species being destroyed + Pair rates = null; + Variable var = action.getVar(); + MacroscopicRateConstant mrc = null; + JumpProcessRateDefinition particleRateDefinition = lpjp.getParticleRateDefinition(); + if(particleRateDefinition instanceof MacroscopicRateConstant) { + mrc = (MacroscopicRateConstant)particleRateDefinition; + } else { + throw new RuntimeException("Rate definition must be MacroscopicRateConstant"); + } + Expression kDecay = null; + try { + kDecay = mrc.getExpression(); + } catch(Exception e) { + throw new RuntimeException("Reaction Rate expression is wrong"); + } + if(creationDecayVariableMap.containsKey(var)) { + rates = creationDecayVariableMap.get(var); + if(!rates.two.equals("0")) { + throw new RuntimeException("Cannot have multiple decay reactions for the same Variable"); + } + Expression exp = null; + String decayRate = null; + try { + exp = MathUtilities.substituteFunctions(kDecay, mathDescription, false); + } catch (ExpressionException e) { + throw new RuntimeException("kDecay substitution failed, " + e.getMessage()); + } + try { + double rate = exp.evaluateConstant(); + decayRate = Double.toString(rate); + } catch (Exception e) { + throw new RuntimeException("rate must be a number"); + } + rates = new Pair<> (rates.one, decayRate); + } else { + throw new RuntimeException("Variable missing in the variables map"); + } + creationDecayVariableMap.put(var, rates); + } + } + } + } + for (Map.Entry> entry : creationDecayVariableMap.entrySet()) { + Variable var = entry.getKey(); + if(!(var instanceof ParticleSpeciesPattern)) { + continue; + } + ParticleSpeciesPattern psp = (ParticleSpeciesPattern)var; + ParticleMolecularTypePattern particleMolecularTypePattern = psp.getParticleMolecularTypePatterns().get(0); + ParticleMolecularType pmt = particleMolecularTypePattern.getMolecularType(); + Pair rates = entry.getValue(); + sb.append("'").append(pmt.getName()).append("' : ") + .append("kcreate ").append(rates.one).append(" ") + .append("kdecay ").append(rates.two); + sb.append("\n"); + } + return; + } + + private static void writeSpeciesFileInfo(StringBuilder sb) { + for( Map.Entry entry : particlePropertiesMap.entrySet()) { + ParticleProperties pp = entry.getKey(); + Variable var = pp.getVariable(); + if(!(var instanceof ParticleSpeciesPattern)) { + continue; // only interested in ParticleSpeciesPattern + } + ParticleSpeciesPattern psp = (ParticleSpeciesPattern)var; + ParticleMolecularTypePattern particleMolecularTypePattern = psp.getParticleMolecularTypePatterns().get(0); + ParticleMolecularType pmt = particleMolecularTypePattern.getMolecularType(); + if(SpeciesContextSpec.SourceMoleculeString.equals(pmt.getName()) || SpeciesContextSpec.SinkMoleculeString.equals(pmt.getName())) { + continue; + } + sb.append("MOLECULE: " + pmt.getName() + " " + getFilename()); + sb.append("\n"); + } + } + + private static void writeSpeciesInfo(StringBuilder sb) { + for( Map.Entry entry : particlePropertiesMap.entrySet()) { + ParticleProperties pp = entry.getKey(); + SubDomain subDomain = entry.getValue(); + + ArrayList particleInitialConditions = pp.getParticleInitialConditions(); + Variable var = pp.getVariable(); + if(!(var instanceof ParticleSpeciesPattern)) { + continue; + } + ParticleSpeciesPattern psp = (ParticleSpeciesPattern)var; + if(psp.getParticleMolecularTypePatterns().size() != 1) { + throw new RuntimeException("A seed species size must be exactly one molecule"); + } + ParticleMolecularTypePattern particleMolecularTypePattern = psp.getParticleMolecularTypePatterns().get(0); + if(!(particleMolecularTypePattern.getMolecularType() instanceof LangevinParticleMolecularType)) { + throw new RuntimeException("LangevinParticleMolecularType expected"); + } + LangevinParticleMolecularType lpmt = (LangevinParticleMolecularType)particleMolecularTypePattern.getMolecularType(); + String spName = var.getName(); + String moleculeName = lpmt.getName(); + + if(particleInitialConditions.size() != 1) { + throw new RuntimeException("One initial condition per variable is required"); + } + if(!(particleInitialConditions.get(0) instanceof ParticleInitialConditionCount)) { + throw new RuntimeException("Count initial condition is required"); + } + ParticleInitialConditionCount pic = (ParticleInitialConditionCount)particleInitialConditions.get(0); + Expression count = pic.getCount(); + String scount; + try { + Expression exp = MathUtilities.substituteFunctions(count, mathDescription, true); + exp = exp.flatten(); + double ddd = exp.evaluateConstant(); + scount = Integer.toString((int)ddd); + } catch (Exception e) { + throw new RuntimeException("Initial concentration must be a number"); + } + + int dimension = geometryContext.getGeometry().getDimension(); + + sb.append("MOLECULE: \"" + lpmt.getName() + "\" " + subDomain.getName() + + " Number " + scount + + // number of site types and number of sites is the same for the vcell implementation of springsalad + " Site_Types " + lpmt.getComponentList().size() + " Total" + "_Sites " + lpmt.getComponentList().size() + + " Total_Links " + lpmt.getInternalLinkSpec().size() + " is2D " + (dimension == 2 ? true : false)); // TODO: is2D must mean if the molecule is "flat" on YZ axis projection + sb.append("\n"); + sb.append("{"); + sb.append("\n"); + + for(ParticleMolecularComponent pmc : lpmt.getComponentList()) { + if(!(pmc instanceof LangevinParticleMolecularComponent)) { + throw new RuntimeException("LangevinParticleMolecularComponent required"); + } + LangevinParticleMolecularComponent lpmc = (LangevinParticleMolecularComponent)pmc; + sb.append(" "); + lpmc.writeType(sb); + } + sb.append("\n"); + for(ParticleMolecularComponent pmc : lpmt.getComponentList()) { + // a few lines that follow are needed to extract the initial state from the ParticleMolecularComponentPattern + ParticleMolecularComponentPattern pmcp = particleMolecularTypePattern.getMolecularComponentPattern(pmc); + ParticleComponentStatePattern pcsp = pmcp.getComponentStatePattern(); + ParticleComponentStateDefinition pcsd = pcsp.getParticleComponentStateDefinition(); + String initialState = pcsd.getName(); + LangevinParticleMolecularComponent lpmc = (LangevinParticleMolecularComponent)pmc; + sb.append(" "); + lpmc.writeSite(sb, lpmt.getComponentList().indexOf(lpmc), initialState); + } + sb.append("\n"); + Set> internalLinkSpec = lpmt.getInternalLinkSpec(); + for(Pair pair : lpmt.getInternalLinkSpec()) { + sb.append(" "); + // we take advantage of the fact that there is one to one relationship between molecular component (site type) and site + int indexOne = lpmt.getComponentList().indexOf(pair.one); + int indexTwo = lpmt.getComponentList().indexOf(pair.two); + sb.append("LINK: Site " + indexOne + " ::: Site " + indexTwo); + sb.append("\n"); + } + + sb.append("\n"); + sb.append(" Initial_Positions: "); + if(InitialLocationRandom) { + sb.append(InitialLocationRandomString); + sb.append("\n"); + } + sb.append("}"); + sb.append("\n"); + sb.append("\n"); + } + System.out.println(sb.toString()); + return; + } + + private static String getFilename() { // SpringSaLaD specific, external file with molecule information + return null; // not implemented + } + + private static double evaluateConstant(Expression expression, SimulationSymbolTable simulationSymbolTable) throws MathException, ExpressionException{ + Expression subExp = simulationSymbolTable.substituteFunctions(expression); + double value = subExp.evaluateConstant(); + return value; + } +} diff --git a/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinSolver.java b/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinSolver.java new file mode 100644 index 0000000000..4fd18634c3 --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/solver/langevin/LangevinSolver.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package org.vcell.solver.langevin; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TreeMap; + +import org.vcell.util.exe.ExecutableException; + +import cbit.vcell.messaging.server.SimulationTask; +import cbit.vcell.solver.DefaultOutputTimeSpec; +import cbit.vcell.solver.LangevinSimulationOptions; +import cbit.vcell.solver.NFsimSimulationOptions; +import cbit.vcell.solver.OutputTimeSpec; +import cbit.vcell.solver.SolverDescription; +import cbit.vcell.solver.SolverException; +import cbit.vcell.solver.SolverUtilities; +import cbit.vcell.solver.TimeBounds; +import cbit.vcell.solver.UniformOutputTimeSpec; +import cbit.vcell.solver.server.SimulationMessage; +import cbit.vcell.solver.server.SolverStatus; +import cbit.vcell.solvers.ApplicationMessage; +import cbit.vcell.solvers.MathExecutable; +import cbit.vcell.solvers.SimpleCompiledSolver; + +/** + * Langevin solver + * + */ +public class LangevinSolver extends SimpleCompiledSolver { + + private String logFileString = null; // logfile name as it appears in argument list + // it is the value of getLogFilename(), but it's set late + + public LangevinSolver(SimulationTask simTask, java.io.File directory, + boolean bMsging) throws SolverException { + super(simTask, directory, bMsging); + } + + /** + * Insert the method's description here. Creation date: (7/13/2006 9:00:41 + * AM) + */ + public void cleanup() { + } + + /** + * show progress. Creation date: (7/13/2006 9:00:41 AM) + * + * @return cbit.vcell.solvers.ApplicationMessage + * @param message + * java.lang.String + */ + protected ApplicationMessage getApplicationMessage(String message) { + String SEPARATOR = ":"; + String DATA_PREFIX = "data:"; + String PROGRESS_PREFIX = "progress:"; + if (message.startsWith(DATA_PREFIX)) { + double timepoint = Double.parseDouble(message.substring(message.lastIndexOf(SEPARATOR) + 1)); + setCurrentTime(timepoint); + return new ApplicationMessage(ApplicationMessage.DATA_MESSAGE, getProgress(), timepoint, null, message); + } else if (message.startsWith(PROGRESS_PREFIX)) { + String progressString = message.substring(message.lastIndexOf(SEPARATOR) + 1, message.indexOf("%")); + double progress = Double.parseDouble(progressString) / 100.0; + // double startTime = + // getSimulation().getSolverTaskDescription().getTimeBounds().getStartingTime(); + // double endTime = + // getSimulation().getSolverTaskDescription().getTimeBounds().getEndingTime(); + // setCurrentTime(startTime + (endTime-startTime)*progress); + return new ApplicationMessage(ApplicationMessage.PROGRESS_MESSAGE, progress, -1, null, message); + } else { + throw new RuntimeException("unrecognized message"); + } + } + + /** + * This method takes the place of the old runUnsteady()... + */ + protected void initialize() throws SolverException { + if (lg.isTraceEnabled()) lg.trace("LangevinSolver.initialize()"); + fireSolverStarting(SimulationMessage.MESSAGE_SOLVEREVENT_STARTING_INIT); + writeFunctionsFile(); + + + // TODO: make the input file and the results folder the way langevin wants them + // results folder: Simulation0_SIM_FOLDER + // input file: Simulation0_SIM.txt + String saveDirectory = getSaveDirectory().getPath(); + String simulationJobID = simTask.getSimulationJob().getSimulationJobID(); + String baseName = getBaseName(); + String resultsFolder = baseName + "SIM_FOLDER"; + String inputFile = baseName + "SIM.txt"; + + // aici + + + + + + String inputFilename = getInputFilename(); + if (lg.isTraceEnabled()) lg.trace("LangevinSolver.initialize() inputFilename = " + getInputFilename()); + + setSolverStatus(new SolverStatus(SolverStatus.SOLVER_RUNNING, SimulationMessage.MESSAGE_SOLVER_RUNNING_INPUT_FILE)); + fireSolverStarting(SimulationMessage.MESSAGE_SOLVEREVENT_STARTING_INPUT_FILE); + + try (PrintWriter pw = new PrintWriter(inputFilename)) { + // here we create the langevin input file (and any other file / directory that may be needed) + LangevinFileWriter stFileWriter = new LangevinFileWriter(pw, simTask, bMessaging); + stFileWriter.write(); + } catch (Exception e) { + setSolverStatus(new SolverStatus(SolverStatus.SOLVER_ABORTED, + SimulationMessage.solverAborted("Could not generate input file: " + e.getMessage()))); + e.printStackTrace(System.out); + throw new SolverException(e.getMessage()); + } + + PrintWriter lg = null; + String logFilename = getLogFilename(); + String outputFilename = getOutputFilename(); + try { + lg = new PrintWriter(logFilename); + String shortOutputFilename = outputFilename.substring(1 + outputFilename.lastIndexOf("\\")); + lg.println(IDA_DATA_IDENTIFIER + "\n" + shortOutputFilename); + } catch (Exception e) { + setSolverStatus(new SolverStatus(SolverStatus.SOLVER_ABORTED, + SimulationMessage.solverAborted("Could not generate log file: " + e.getMessage()))); + e.printStackTrace(System.out); + throw new SolverException(e.getMessage()); + } finally { + if (lg != null) { + lg.close(); + } + } + + /* + what solver gets from springsalad ui + C:\TEMP\ssld-eclipse\ssld-eclipse_SIMULATIONS\Simulation0_SIM.txt + 1 + C:\TEMP\ssld-eclipse\ssld-eclipse_SIMULATIONS\Simulations0_OutStream_0.txt + + what we give the solver + C:\Users\Vasilescu\.vcell\simdata\temp\SimID_1504782733_0_.langevinInput + 1 + C:\Users\Vasilescu\.vcell\simdata\temp\SimID_1504782733_0_.log + */ + + setSolverStatus(new SolverStatus(SolverStatus.SOLVER_RUNNING, + SimulationMessage.MESSAGE_SOLVER_RUNNING_START)); + // get executable path+name. + String[] mathExecutableCommand = getMathExecutableCommand(); + File saveDirectoryFile = getSaveDirectory(); // working directory (not needed?) + +// mathExecutableCommand[2] = "--output-log=C:\\TEMP\\test\\ssld-eclipse_SIMULATIONS\\Simulations0_OutStream_0.txt"; +// mathExecutableCommand[3] = "C:\\TEMP\\test\\ssld-eclipse_SIMULATIONS\\Simulation0_SIM.txt"; + + + MathExecutable me = new MathExecutable(mathExecutableCommand, saveDirectoryFile); + setMathExecutable(me); + +// setLogFileString("C:\\TEMP\\test\\ssld-eclipse_SIMULATIONS\\Simulations0_OutStream_0.txt"); + setLogFileString(getLogFilename()); // variable is set "late" TODO: may be not needed + } + + private String getInputFilename() { + return getBaseName() + LANGEVIN_INPUT_FILE_EXTENSION; + } + + private String getLogFilename() { + return getBaseName() + LANGEVIN_OUTPUT_LOG_EXTENSION; + } + + private String getOutputFilename() { + return getBaseName() + LANGEVIN_OUTPUT_FILE_EXTENSION; + } + public String getSpeciesOutputFilename() { + return getBaseName() + ".species"; + } + + @Override + protected String[] getMathExecutableCommand() { + String executableName = null; + try { + executableName = SolverUtilities.getExes(SolverDescription.Langevin)[0].getAbsolutePath(); + }catch (IOException e){ + throw new RuntimeException("failed to get executable for solver "+SolverDescription.Langevin.getDisplayLabel()+": "+e.getMessage(),e); + } + String inputFilename = getInputFilename(); + String outputFilename = getOutputFilename(); + String speciesOutputFilename = getSpeciesOutputFilename(); + String logFilename = getLogFilename(); + String logFileOption = "--output-log=" + logFilename; + + LangevinSimulationOptions lso = simTask.getSimulation().getSolverTaskDescription().getLangevinSimulationOptions(); + int runIndex = lso.getRunIndex(); // run index + + ArrayList cmds = new ArrayList(); + cmds.add(executableName); // executable + cmds.add("simulate"); // the new langevin solver made by jim wants this argument + cmds.add(logFileOption); // used for solver to send status info to client (3rd argument); + cmds.add(inputFilename); // first argument + cmds.add(runIndex + ""); // second argument + + return cmds.toArray(new String[cmds.size()]); + } + + @Override + public String translateSimulationMessage(String simulationMessage) { + + // TODO: translate langevin solver error codes into nice user messages + + return simulationMessage; + } + + public void propertyChange(java.beans.PropertyChangeEvent event) { + super.propertyChange(event); + + if (event.getSource() == getMathExecutable() + && event.getPropertyName().equals("applicationMessage")) { + String messageString = (String) event.getNewValue(); + if (messageString == null || messageString.length() == 0) { + return; + } + ApplicationMessage appMessage = getApplicationMessage(messageString); + if (appMessage != null + && appMessage.getMessageType() == ApplicationMessage.PROGRESS_MESSAGE) { + fireSolverPrinted(getCurrentTime()); + } + } + } + + public static void main(String[] args) { + + try { + + } catch (Exception e) { + e.printStackTrace(); + + } finally { + + } + } + + public String getLogFileString() { + return logFileString; + } + + public void setLogFileString(String logFileString) { + this.logFileString = logFileString; + } +} + diff --git a/vcell-core/src/main/java/org/vcell/solver/nfsim/NFSimSolver.java b/vcell-core/src/main/java/org/vcell/solver/nfsim/NFSimSolver.java index c3f0dfc7e1..6289d85c57 100644 --- a/vcell-core/src/main/java/org/vcell/solver/nfsim/NFSimSolver.java +++ b/vcell-core/src/main/java/org/vcell/solver/nfsim/NFSimSolver.java @@ -42,9 +42,8 @@ import cbit.vcell.solvers.SimpleCompiledSolver; /** - * Gibson solver Creation date: (7/13/2006 9:00:41 AM) + * NFSim solver * - * @author: Tracy LI */ public class NFSimSolver extends SimpleCompiledSolver { @@ -53,15 +52,11 @@ public NFSimSolver(SimulationTask simTask, java.io.File directory, super(simTask, directory, bMsging); } - /** - * Insert the method's description here. Creation date: (7/13/2006 9:00:41 - * AM) - */ public void cleanup() { } /** - * show progress. Creation date: (7/13/2006 9:00:41 AM) + * show progress * * @return cbit.vcell.solvers.ApplicationMessage * @param message diff --git a/vcell-core/src/main/java/org/vcell/util/document/BioModelChildSummary.java b/vcell-core/src/main/java/org/vcell/util/document/BioModelChildSummary.java index bd11bc2c43..3333a3a180 100644 --- a/vcell-core/src/main/java/org/vcell/util/document/BioModelChildSummary.java +++ b/vcell-core/src/main/java/org/vcell/util/document/BioModelChildSummary.java @@ -43,6 +43,7 @@ public enum MathType { RuleBased("RuleBased"), Stochastic("Stochastic"), Deterministic("Deterministic"), + SpringSaLaD("SpringSaLaD"), Unknown("Unknown"); private String description = null; diff --git a/vcell-core/src/main/java/org/vcell/util/springsalad/Colors.java b/vcell-core/src/main/java/org/vcell/util/springsalad/Colors.java new file mode 100644 index 0000000000..f71e889af2 --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/util/springsalad/Colors.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package org.vcell.util.springsalad; + +import java.awt.Color; +import java.util.HashMap; + + +public class Colors { + + // Need 24 named colors + public final static String REDSTRING = "RED"; // 1 + public final static String BLUESTRING = "BLUE"; + public final static String LIMESTRING = "LIME"; + public final static String ORANGESTRING = "ORANGE"; + public final static String CYANSTRING = "CYAN"; // 5 + public final static String MAGENTASTRING = "MAGENTA"; + public final static String PINKSTRING = "PINK"; + public final static String YELLOWSTRING = "YELLOW"; + public final static String GRAYSTRING = "GRAY"; + public final static String PURPLESTRING = "PURPLE"; //10 + public final static String GREENSTRING = "GREEN"; + public final static String MAROONSTRING = "MAROON"; + public final static String NAVYSTRING = "NAVY"; + public final static String OLIVESTRING = "OLIVE"; + public final static String TEALSTRING = "TEAL"; // 15 + public final static String LIMEGREENSTRING = "LIME_GREEN"; + public final static String GOLDSTRING = "GOLD"; + public final static String DARKGREENSTRING = "DARK_GREEN"; + public final static String CRIMSONSTRING = "CRIMSON"; + public final static String DARKVIOLETSTRING = "DARK_VIOLET"; //20 + public final static String VIOLETSTRING = "VIOLET"; + public final static String SLATEBLUESTRING = "SLATE_BLUE"; + public final static String LIGHTCYANSTRING = "LIGHT_CYAN"; + public final static String DARKCYANSTRING = "DARK_CYAN"; + public final static String LIGHTGRAYSTRING = "LIGHT_GRAY"; // 25 + public final static String DARKGRAYSTRING = "DARK_GRAY"; + public final static String WHITESTRING = "WHITE"; + public final static String BLACKSTRING = "BLACK"; + + public final static String [] COLORNAMES = { REDSTRING, BLUESTRING, + LIMESTRING, ORANGESTRING, CYANSTRING, MAGENTASTRING, PINKSTRING, + YELLOWSTRING, GRAYSTRING, PURPLESTRING, GREENSTRING, MAROONSTRING, + NAVYSTRING, OLIVESTRING, TEALSTRING, LIMEGREENSTRING, GOLDSTRING, + DARKGREENSTRING, CRIMSONSTRING, DARKVIOLETSTRING, VIOLETSTRING, SLATEBLUESTRING, + LIGHTCYANSTRING, DARKCYANSTRING, LIGHTGRAYSTRING, DARKGRAYSTRING, + WHITESTRING, BLACKSTRING}; + + public final static NamedColor RED = new NamedColor(REDSTRING, Color.RED); + public final static NamedColor BLUE = new NamedColor(BLUESTRING, Color.BLUE); + public final static NamedColor LIME = new NamedColor(LIMESTRING, Color.GREEN); + public final static NamedColor ORANGE = new NamedColor(ORANGESTRING, Color.ORANGE); + public final static NamedColor CYAN = new NamedColor(CYANSTRING, Color.CYAN); + public final static NamedColor MAGENTA = new NamedColor(MAGENTASTRING, Color.MAGENTA); + public final static NamedColor PINK = new NamedColor(PINKSTRING, Color.PINK); + public final static NamedColor YELLOW = new NamedColor(YELLOWSTRING, Color.YELLOW); + public final static NamedColor GRAY = new NamedColor(GRAYSTRING, Color.GRAY); + public final static NamedColor PURPLE = new NamedColor(PURPLESTRING, new Color(128, 0, 128)); + public final static NamedColor GREEN = new NamedColor(GREENSTRING, new Color(0, 128, 0)); + public final static NamedColor MAROON = new NamedColor(MAROONSTRING, new Color(128, 0, 0)); + public final static NamedColor NAVY = new NamedColor(NAVYSTRING, new Color(0, 0, 128)); + public final static NamedColor OLIVE = new NamedColor(OLIVESTRING, new Color(128, 128, 0)); + public final static NamedColor TEAL = new NamedColor(TEALSTRING, new Color(0, 128, 128)); + public final static NamedColor LIMEGREEN = new NamedColor(LIMEGREENSTRING, new Color(50, 205, 50)); + public final static NamedColor GOLD = new NamedColor(GOLDSTRING, new Color(255, 215, 0)); + public final static NamedColor DARKGREEN = new NamedColor(DARKGREENSTRING, new Color(0, 100, 0)); + public final static NamedColor CRIMSON = new NamedColor(CRIMSONSTRING, new Color(220, 20, 60)); + public final static NamedColor DARKVIOLET = new NamedColor(DARKVIOLETSTRING, new Color(148, 0, 211)); + public final static NamedColor VIOLET = new NamedColor(VIOLETSTRING, new Color(238, 130, 238)); + public final static NamedColor SLATEBLUE = new NamedColor(SLATEBLUESTRING, new Color(106, 90, 205)); + public final static NamedColor LIGHTCYAN = new NamedColor(LIGHTCYANSTRING, new Color(224, 255, 255)); + public final static NamedColor DARKCYAN = new NamedColor(DARKCYANSTRING, new Color(0, 139, 139)); + + public final static NamedColor LIGHTGRAY = new NamedColor(LIGHTGRAYSTRING, Color.LIGHT_GRAY); + public final static NamedColor DARKGRAY = new NamedColor(DARKGRAYSTRING, Color.DARK_GRAY); + public final static NamedColor WHITE = new NamedColor(WHITESTRING, Color.WHITE); + public final static NamedColor BLACK = new NamedColor(BLACKSTRING, Color.BLACK); + + public final static NamedColor [] COLORARRAY = new NamedColor[] { RED, BLUE, + LIME, ORANGE, CYAN, MAGENTA, PINK, YELLOW, GRAY, PURPLE, GREEN, + MAROON, NAVY, OLIVE, TEAL, LIMEGREEN, GOLD, DARKGREEN, CRIMSON, + DARKVIOLET, VIOLET, SLATEBLUE, LIGHTCYAN, DARKCYAN, LIGHTGRAY, + DARKGRAY, WHITE, BLACK}; + + + public static NamedColor getColorByName(String name) { + NamedColor namedColor = null; + for (NamedColor nColor : COLORARRAY) { + if (name.equals(nColor.getName())) { + namedColor = nColor; + break; + } + } + return namedColor; + } + +} diff --git a/vcell-core/src/main/java/org/vcell/util/springsalad/IOHelp.java b/vcell-core/src/main/java/org/vcell/util/springsalad/IOHelp.java new file mode 100644 index 0000000000..a3ceef5e48 --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/util/springsalad/IOHelp.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package org.vcell.util.springsalad; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Scanner; + + +public class IOHelp { + public final static DecimalFormat [] DF = new DecimalFormat[]{new DecimalFormat("0."), + new DecimalFormat("0.0"), new DecimalFormat("0.00"), new DecimalFormat("0.000"), + new DecimalFormat("0.0000"), new DecimalFormat("0.00000"), + new DecimalFormat("0.000000"), new DecimalFormat("0.0000000"), + new DecimalFormat("0.00000000") + }; + + public final static DecimalFormat scientificFormat = new DecimalFormat("0.00#E0"); + + public final static String ERROR = "ERROR"; + public final static String SEPARATOR = System.getProperty("file.separator"); + + // This method should be used when we know that the next few entries of the + // scanner are a name in quotes, and we want to extract that collection of + // individual strings as a single string, and we want to drop the quotes. + public static String getNameInQuotes(Scanner sc) { + StringBuilder sb = new StringBuilder(); + String s = sc.next(); + char quote = '"'; + char singlequote = '\''; + char rightbracket = '}'; + if(!(s.charAt(0) == quote || s.charAt(0) == singlequote)) { + if(s.charAt(0) == rightbracket){ + return s; + } else if(s.equals("***")){ + return s; + } else { + System.out.println("Helper.getNameInQuotes() was started on " + s + + ", a string that did not begin with a quote."); + return ERROR; + } + } else { + s = s.substring(1,s.length()); + // Now look to see if it has a trailing quote + if(s.charAt(s.length()-1) == quote || s.charAt(s.length()-1) == singlequote) { + s = s.substring(0,s.length()-1); + return s; + } else { + sb.append(s); + while(sc.hasNext()) { + String s1 = sc.next(); + if(s1.charAt(s1.length()-1) == quote || s1.charAt(s1.length()-1) == singlequote) { + s1 = s1.substring(0,s1.length()-1); + sb.append(" ").append(s1); + break; + } else { + sb.append(" ").append(s1); + } + } + return sb.toString(); + } + } + } + + public static Scanner makeScanner(ArrayList stringArray) { + StringBuilder sb = new StringBuilder(); + for (String string : stringArray) { + sb.append(string); + sb.append("\n"); + } + return new Scanner(sb.toString()); + } + + public static File setFileType(File file, String type) { + String filename = file.getName(); + if(filename.length() > 4) { + if(filename.charAt(filename.length()-4) == '.') { + String end = filename.substring(filename.length() - 3); + if(!end.equals(type)){ + filename = filename.substring(0,filename.length()-3) + type; + } + } else { + filename = filename + "." + type; + } + } else { + filename = filename + "." + type; + } + return new File(file.getParentFile(), filename); + } + + public static String printArray(Object [] objects) { + StringBuilder sb = new StringBuilder("{"); + for(int i = 0;i list, int decimalDigits) { + StringBuilder sb = new StringBuilder(); + for(Double d : list){ + sb.append(DF[decimalDigits].format(d)).append(" "); + } + return sb.toString(); + } + + public static void removeRecursive(Path path) throws IOException { + if(Files.exists(path)) { + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + // try to delete the file anyway, even if its attributes + // could not be read, since delete-only access is + // theoretically possible + Files.delete(file); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + if (exc == null) { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } else { + // directory iteration failed; propagate exception + throw exc; + } + } + }); + } + } + + +} diff --git a/vcell-core/src/main/java/org/vcell/util/springsalad/NamedColor.java b/vcell-core/src/main/java/org/vcell/util/springsalad/NamedColor.java new file mode 100644 index 0000000000..0de30a1194 --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/util/springsalad/NamedColor.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 1999-2011 University of Connecticut Health Center + * + * Licensed under the MIT License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.opensource.org/licenses/mit-license.php + */ + +package org.vcell.util.springsalad; + +import java.awt.Color; +import java.io.Serializable; + +@SuppressWarnings("serial") +public class NamedColor implements Serializable { + private final Color color; + private final String name; + + public NamedColor(String name, Color color) { + this.name = name; + this.color = color; + } + + public Color getColor() { + return color; + } + public String getName() { + return name; + } + @Override + public String toString() { + return name; + } + +} diff --git a/vcell-core/src/test/java/cbit/vcell/biomodel/ModelUnitConverterTest.java b/vcell-core/src/test/java/cbit/vcell/biomodel/ModelUnitConverterTest.java index ea4d94f5a4..5242a08c45 100644 --- a/vcell-core/src/test/java/cbit/vcell/biomodel/ModelUnitConverterTest.java +++ b/vcell-core/src/test/java/cbit/vcell/biomodel/ModelUnitConverterTest.java @@ -1,5 +1,8 @@ package cbit.vcell.biomodel; +import cbit.image.ImageException; +import cbit.vcell.geometry.GeometryException; +import cbit.vcell.mapping.IllegalMappingException; import cbit.vcell.mapping.MappingException; import cbit.vcell.mapping.SimulationContext; import cbit.vcell.math.Constant; @@ -61,7 +64,7 @@ public void test_unit_factors() throws ExpressionException { } @Test - public void test_MathOverrides_unit_factors_are_idempotent() throws IOException, XmlParseException, ExpressionException, MatrixException, ModelException, MathException, MappingException, PropertyVetoException { + public void test_MathOverrides_unit_factors_are_idempotent() throws IOException, XmlParseException, ExpressionException, MatrixException, ModelException, MathException, MappingException, PropertyVetoException, GeometryException, ImageException, IllegalMappingException { final double Kf_value_orig = 2.0; final double Kr_value_orig = 3.0; final double Kf_value_override_orig = 22.0; diff --git a/vcell-core/src/test/java/cbit/vcell/biomodel/SpringSaLaDGoodReactionsTest.java b/vcell-core/src/test/java/cbit/vcell/biomodel/SpringSaLaDGoodReactionsTest.java new file mode 100644 index 0000000000..68ca561813 --- /dev/null +++ b/vcell-core/src/test/java/cbit/vcell/biomodel/SpringSaLaDGoodReactionsTest.java @@ -0,0 +1,286 @@ +package cbit.vcell.biomodel; + +import cbit.image.ImageException; +import cbit.vcell.geometry.Geometry; +import cbit.vcell.geometry.GeometryException; +import cbit.vcell.mapping.IllegalMappingException; +import cbit.vcell.mapping.MappingException; +import cbit.vcell.mapping.MolecularInternalLinkSpec; +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; +import cbit.vcell.mapping.SpeciesContextSpec; +import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; +import cbit.vcell.math.CompartmentSubDomain; +import cbit.vcell.math.LangevinParticleMolecularType; +import cbit.vcell.math.MathCompareResults; +import cbit.vcell.math.MathDescription; +import cbit.vcell.math.MathException; +import cbit.vcell.math.ParticleJumpProcess; +import cbit.vcell.math.ParticleMolecularType; +import cbit.vcell.matrix.MatrixException; +import cbit.vcell.messaging.server.SimulationTask; +import cbit.vcell.model.ModelException; +import cbit.vcell.model.ReactionRule; +import cbit.vcell.model.SpeciesContext; +import cbit.vcell.parser.Expression; +import cbit.vcell.parser.ExpressionException; +import cbit.vcell.resource.ResourceUtil; +import cbit.vcell.solver.LangevinSimulationOptions; +import cbit.vcell.solver.Simulation; +import cbit.vcell.solver.SimulationJob; +import cbit.vcell.solver.SimulationSymbolTable; +import cbit.vcell.solver.SolverDescription; +import cbit.vcell.solver.SolverException; +import cbit.vcell.solver.SolverUtilities; +import cbit.vcell.solver.server.Solver; +import cbit.vcell.solver.server.SolverFactory; +import cbit.vcell.xml.XMLSource; +import cbit.vcell.xml.XmlHelper; +import cbit.vcell.xml.XmlParseException; +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.vcell.solver.langevin.LangevinLngvWriter; +import org.vcell.solver.langevin.LangevinSolver; +import org.vcell.solver.smoldyn.SmoldynSolver; +import org.vcell.test.Fast; +import org.vcell.util.Issue; +import org.vcell.util.IssueContext; +import org.vcell.util.Issue.Severity; +import org.vcell.util.document.KeyValue; +import org.vcell.util.document.User; +import org.vcell.util.document.BioModelChildSummary.MathType; + +import java.beans.PropertyVetoException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Set; +import java.util.Vector; + +public class SpringSaLaDGoodReactionsTest { + + private static final String reactionTestString = "'r0' :: 'MT0' : 'Site1' : 'state0' --> 'state1' Rate 50.0 Condition Free"; + + + /* --------------------------------------------------------------------------------------------------------------------------- + * This test will check the compatibility with springsalad requirements as follows: + * - compartment number and name + * - seed species and their molecules + */ + @Test + public void test_springsalad_model_bad() throws IOException, XmlParseException, PropertyVetoException, ExpressionException, GeometryException, + ImageException, IllegalMappingException, MappingException { + +// System.out.println("start springsalad test for compartments, seed species and molecules"); + BioModel bioModel = getBioModelFromResource("Spring_model_bad.vcml"); + Assert.assertTrue("expecting non-null biomodel", bioModel != null ? true : false); + SimulationContext simContext = bioModel.addNewSimulationContext("Application", SimulationContext.Application.SPRINGSALAD); + Assert.assertTrue("expecting non-null simulation context", simContext != null ? true : false); + + Application appType = simContext.getApplicationType(); + Assert.assertTrue("expecting SPRINGSALAD application type", (appType != null && appType == Application.SPRINGSALAD) ? true : false); + + // we want to delete the link between the sites of this species and test that the corresponding issue is triggered + // after this change we should have 8 warning issues (7 otherwise) + SpeciesContext scCandidate = simContext.getModel().getSpeciesContext("MT2"); + SpeciesContextSpec scsCandidate = simContext.getReactionContext().getSpeciesContextSpec(scCandidate); + Set internalLinkSet = scsCandidate.getInternalLinkSet(); + int size = internalLinkSet.size(); + Assert.assertTrue("expecting one link for species 'MT2'", (internalLinkSet.size() == 1) ? true : false); + internalLinkSet.clear(); + + Vector issueList = new Vector(); + IssueContext issueContext = new IssueContext(); + simContext.getModel().gatherIssues(issueContext, issueList); + simContext.gatherIssues(issueContext, issueList, true); // bIgnoreMathDescription == true + int numErrors = checkIssuesBySeverity(issueList, Issue.Severity.ERROR); + int numWarnings = checkIssuesBySeverity(issueList, Issue.Severity.WARNING); + Assert.assertTrue("expecting 2 errors and 8 warnings for this model", (numErrors == 2 && numWarnings == 8) ? true : false); + + /* We should detect the following: + + c0: SpringSaLaD requires exactly 3 Structures: one Membrane and two Features (Compartments) + c0: 'c0' not legal identifier for SpringSaLaD applications. Try using 'Intracellular' or 'Extracellular'. + s0: SpringSaLaD requires the MolecularType to have at least one Site. + s1: Each Site must have at least one State. + s2: Internal Links are possible only when the Molecule has at least 2 sites. + s2: The Species and the Molecular Type must share the same name. + MT2: Link chain within the molecule has at least one discontinuity. + Sink: SpringSaLaD reserved Molecules 'Source' and 'Sink' must not have any sites defined + s5: There must be a biunivocal correspondence between the Species and the associated MolecularType. + s6: There must be a biunivocal correspondence between the Species and the associated MolecularType. + + TODO: we may consider initializing all the issue string as static named variable and compare identity, but it will probably be overkill + TODO: add membrane molecules and test for Anchor; also test for Anchor missing in non-membrane molecules + */ + } + + /* ------------------------------------------------------------------------------------------------------------------------- + * This test exercises a complete set of springsalad-incompatible reactions + * All the reactions in this example should be marked as incompatible, either Error or Warning issues. + */ + @Test + public void test_springsalad_bad_reactions() throws IOException, XmlParseException, PropertyVetoException, ExpressionException, GeometryException, + ImageException, IllegalMappingException, MappingException { + +// System.out.println("start springsalad test for incompatible reactions"); + BioModel bioModel = getBioModelFromResource("Spring_reactions_bad.vcml"); + Assert.assertTrue("expecting non-null biomodel", bioModel != null ? true : false); + SimulationContext simContext = bioModel.addNewSimulationContext("Application", SimulationContext.Application.SPRINGSALAD); + Assert.assertTrue("expecting non-null simulation context", simContext != null ? true : false); + + Application appType = simContext.getApplicationType(); + Assert.assertTrue("expecting SPRINGSALAD application type", (appType != null && appType == Application.SPRINGSALAD) ? true : false); + + Geometry geometry = simContext.getGeometry(); + Assert.assertTrue("expecting 3D geometry", geometry.getDimension() == 3 ? true : false); + + Vector issueList = new Vector(); + IssueContext issueContext = new IssueContext(); + simContext.getModel().gatherIssues(issueContext, issueList); + simContext.gatherIssues(issueContext, issueList, true); // bIgnoreMathDescription == true + int numErrors = checkIssuesBySeverity(issueList, Issue.Severity.ERROR); + int numWarnings = checkIssuesBySeverity(issueList, Issue.Severity.WARNING); + Assert.assertTrue("expecting 1 errors and 14 warning issues", (numErrors == 1 && numWarnings == 14) ? true : false); + } + + /* ------------------------------------------------------------------------------------------------------------------------- + * This test exercises a complete set of springsalad-compatible reactions and math generation + * All the reactions in this example should be marked as valid, there should be no error or warning issues whatsoever. + * Math generation should succeed. + */ + @Test + public void test_springsalad_good_reactions() throws IOException, XmlParseException, PropertyVetoException, ExpressionException, GeometryException, + ImageException, IllegalMappingException, MappingException { + +// System.out.println("start springsalad test for all compatible reactions"); + BioModel bioModel = getBioModelFromResource("Spring_reactions_good.vcml"); + Assert.assertTrue("expecting non-null biomodel", bioModel != null ? true : false); + SimulationContext simContext = bioModel.addNewSimulationContext("Application", SimulationContext.Application.SPRINGSALAD); + Assert.assertTrue("expecting non-null simulation context", simContext != null ? true : false); + + Application appType = simContext.getApplicationType(); + Assert.assertTrue("expecting SPRINGSALAD application type", (appType != null && appType == Application.SPRINGSALAD) ? true : false); + + Geometry geometry = simContext.getGeometry(); + Assert.assertTrue("expecting 3D geometry", geometry.getDimension() == 3 ? true : false); + + Vector issueList = new Vector(); + IssueContext issueContext = new IssueContext(); + simContext.getModel().gatherIssues(issueContext, issueList); + simContext.gatherIssues(issueContext, issueList, true); // bIgnoreMathDescription == true + int numErrors = checkIssuesBySeverity(issueList, Issue.Severity.ERROR); + int numWarnings = checkIssuesBySeverity(issueList, Issue.Severity.WARNING); + Assert.assertTrue("expecting no Application error/warning issues", (numErrors == 0 && numWarnings == 0) ? true : false); + + // WARNING!! Debug configuration for this JUnit test required System property "vcell.installDir" + // ex: -Dvcell.installDir=C:\dan\jprojects\git\vcell + bioModel.updateAll(false); + MathDescription mathDescription = simContext.getMathDescription(); + List particleMolecularTypes = mathDescription.getParticleMolecularTypes(); + int count = 0; + for(ParticleMolecularType pmt : particleMolecularTypes) { + if(pmt instanceof LangevinParticleMolecularType) { + count++; + } + } + Assert.assertTrue("expecting 4 LangevinParticleMolecularType entities", count == 4 ? true : false); + + CompartmentSubDomain compartmentSubDomain = mathDescription.getCompartmentSubDomain("Intracellular"); + List particleJumpProcesses = compartmentSubDomain.getParticleJumpProcesses(); + List reactionRuleList = bioModel.getModel().getRbmModelContainer().getReactionRuleList(); + Assert.assertTrue("expecting 9 ReactionRule entities", reactionRuleList.size() == 9 ? true : false); + Assert.assertTrue("expecting 10 ParticleJumpProcess entities", particleJumpProcesses.size() == 10 ? true : false); + + MathType mathType = mathDescription.getMathType(); + Assert.assertTrue("expecting SpringSaLaD math type", (mathType != null && mathType == MathType.SpringSaLaD) ? true : false); + } + + /* ------------------------------------------------------------------------------------------------------------------------------ + * This will construct and initialize a simulation based on the langevin solver and create the input file for running the solver. + * At a later date we'd want to run the solver and compare the results against some expected result set. + */ + @Test + public void test_springsalad_simple_simulation() throws IOException, XmlParseException, PropertyVetoException, ExpressionException, GeometryException, + ImageException, IllegalMappingException, MappingException, SolverException { + + BioModel bioModel = getBioModelFromResource("Spring_transition_free.vcml"); + Assert.assertTrue("expecting non-null biomodel", bioModel != null ? true : false); + SimulationContext simContext = bioModel.addNewSimulationContext("Application", SimulationContext.Application.SPRINGSALAD); + Assert.assertTrue("expecting non-null simulation context", simContext != null ? true : false); + + Application appType = simContext.getApplicationType(); + Assert.assertTrue("expecting SPRINGSALAD application type", (appType != null && appType == Application.SPRINGSALAD) ? true : false); + + Geometry geometry = simContext.getGeometry(); + Assert.assertTrue("expecting 3D geometry", geometry.getDimension() == 3 ? true : false); + + Vector issueList = new Vector(); + IssueContext issueContext = new IssueContext(); + simContext.getModel().gatherIssues(issueContext, issueList); + simContext.gatherIssues(issueContext, issueList, true); // bIgnoreMathDescription == true + int numErrors = checkIssuesBySeverity(issueList, Issue.Severity.ERROR); + int numWarnings = checkIssuesBySeverity(issueList, Issue.Severity.WARNING); + Assert.assertTrue("expecting no Application error/warning issues", (numErrors == 0 && numWarnings == 0) ? true : false); + + // WARNING!! Debug configuration for this JUnit test required System property "vcell.installDir" + // ex: -Dvcell.installDir=C:\dan\jprojects\git\vcell + bioModel.updateAll(false); // this call generates math + MathDescription mathDescription = simContext.getMathDescription(); + Assert.assertTrue("expecting SpringSaLaD math type", (mathDescription.getMathType() != null && mathDescription.getMathType() == MathType.SpringSaLaD) ? true : false); + + + // ------------------------------------------------------------------------------- + Simulation simulation = new Simulation(mathDescription, simContext); + simContext.addSimulation(simulation); + + File localSimDataDir = ResourceUtil.getLocalSimDir(User.tempUser.getName()); + + SimulationJob simJob = new SimulationJob(simulation, 0, null); + SimulationTask simTask = new SimulationTask(simJob, 0); + + + SolverDescription solverDescription = simTask.getSimulation().getSolverTaskDescription().getSolverDescription(); + Assert.assertTrue("expecting non-null SolverDescription", (solverDescription != null) ? true : false); + Assert.assertTrue("expecting databese name 'Langevin'", ("Langevin".equals(solverDescription.getDatabaseName())) ? true : false); + + // generate the input file for the solver and validate it + LangevinSimulationOptions langevinSimulationOptions = simTask.getSimulation().getSolverTaskDescription().getLangevinSimulationOptions(); + int randomSeed = 0; + String langevinLngvString = null; + langevinLngvString = LangevinLngvWriter.writeLangevinLngv(simTask.getSimulation(), randomSeed, langevinSimulationOptions); + Assert.assertTrue("expecting non-null solver input string", (langevinLngvString != null) ? true : false); + Assert.assertTrue("expecting properly formatted transition reaction", (langevinLngvString.contains(reactionTestString)) ? true : false); + + SolverUtilities.prepareSolverExecutable(solverDescription); + // create solver from SolverFactory + Solver solver = SolverFactory.createSolver(localSimDataDir, simTask, false); + Assert.assertTrue("expecting instanceof Langevin solver", (solver instanceof LangevinSolver) ? true : false); + } + + // ========================================================================================================================== + + private static BioModel getBioModelFromResource(String fileName) throws IOException, XmlParseException { + InputStream inputStream = SpringSaLaDGoodReactionsTest.class.getResourceAsStream(fileName); + if (inputStream == null) { + throw new FileNotFoundException("file not found! " + fileName); + } else { + String vcml = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + return XmlHelper.XMLToBioModel(new XMLSource(vcml)); + } + } + public static int checkIssuesBySeverity(Vector issueList, Issue.Severity severity) { + int counter = 0; + for (Issue issue : issueList) { + if (severity == issue.getSeverity()) { + counter++; + } + } + return counter; + } +} diff --git a/vcell-core/src/test/java/cbit/vcell/solver/TestMathDescription.java b/vcell-core/src/test/java/cbit/vcell/solver/TestMathDescription.java index 03847b63dd..532236c686 100644 --- a/vcell-core/src/test/java/cbit/vcell/solver/TestMathDescription.java +++ b/vcell-core/src/test/java/cbit/vcell/solver/TestMathDescription.java @@ -123,5 +123,10 @@ public boolean isRuleBased() { return false; } + @Override + public boolean isLangevin() { + return false; + } + } diff --git a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java b/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java index 6a49013436..d6a1f96a0b 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java @@ -96,11 +96,11 @@ Map knownSEDMLFaults() { faults.put("biomodel_145545992.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_148700996.vcml", SEDML_FAULT.MATH_OVERRIDE_NAMES_DIFFERENT); // simulation 'Full sim' in simContext 'Fast B Compartmental' faults.put("biomodel_154961582.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); - faults.put("biomodel_155016832.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); +// faults.put("biomodel_155016832.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_156134818.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_16763273.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_16804037.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); - faults.put("biomodel_168717401.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); +// faults.put("biomodel_168717401.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_169993006.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_17098642.vcml", SEDML_FAULT.NO_MODELS_IN_OMEX); faults.put("biomodel_17257105.vcml", SEDML_FAULT.MATH_OVERRIDE_NAMES_DIFFERENT); // simulation '1 uM iso' in simContext 'compartmental' @@ -191,7 +191,7 @@ public static Collection testCases() { // new TestCase("biomodel_40882931.vcml", ModelFormat.SBML), // new TestCase("biomodel_40883509.vcml", ModelFormat.SBML), // new TestCase("biomodel_65311813.vcml", ModelFormat.SBML), -// new TestCase("biomodel_82065439.vcml", ModelFormat.SBML) +// new TestCase("biomodel_155016832.vcml", ModelFormat.SBML) // ); } diff --git a/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java b/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java new file mode 100644 index 0000000000..fda9b4ea22 --- /dev/null +++ b/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java @@ -0,0 +1,349 @@ +package org.vcell.sedml; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.jlibsedml.AbstractTask; +import org.jlibsedml.Algorithm; +import org.jlibsedml.ArchiveComponents; +import org.jlibsedml.DataGenerator; +import org.jlibsedml.Libsedml; +import org.jlibsedml.Model; +import org.jlibsedml.OneStep; +import org.jlibsedml.Output; +import org.jlibsedml.SEDMLDocument; +import org.jlibsedml.SedML; +import org.jlibsedml.SteadyState; +import org.jlibsedml.UniformTimeCourse; +import org.jlibsedml.execution.ArchiveModelResolver; +import org.jlibsedml.execution.ModelResolver; +import org.jlibsedml.modelsupport.BioModelsModelsRetriever; +import org.jlibsedml.modelsupport.KisaoOntology; +import org.jlibsedml.modelsupport.KisaoTerm; +import org.jlibsedml.modelsupport.URLResourceRetriever; +import org.vcell.util.ClientTaskStatusSupport; +import org.vcell.util.ProgressDialogListener; + +import cbit.util.xml.VCLogger; +import cbit.util.xml.VCLoggerException; +import cbit.vcell.biomodel.BioModel; +import cbit.vcell.mapping.MathMappingCallbackTaskAdapter; +import cbit.vcell.mapping.SimulationContext; +import cbit.vcell.mapping.SimulationContext.Application; +import cbit.vcell.mapping.SimulationContext.MathMappingCallback; +import cbit.vcell.mapping.SimulationContext.NetworkGenerationRequirements; +import cbit.vcell.resource.ResourceUtil; +import cbit.vcell.solver.Simulation; +import cbit.vcell.solver.SolverDescription; +import cbit.vcell.xml.XMLSource; +import cbit.vcell.xml.XmlHelper; +@Deprecated +public class StandaloneSEDMLTest { + + static ClientTaskStatusSupport progressListener = new ClientTaskStatusSupport() { + @Override + public void setProgress(int progress) { + System.out.println("status: progress : "+progress); + } + + @Override + public void setMessage(String message) { + System.out.println("status: message : "+message); + } + + @Override + public boolean isInterrupted() { + return false; + } + + @Override + public int getProgress() { + return 0; + } + + @Override + public void addProgressDialogListener(ProgressDialogListener progressDialogListener) { + } + }; + + + static VCLogger transLogger = new VCLogger() { + + @Override + public void sendMessage(Priority p, ErrorType et, String message) throws VCLoggerException { + System.err.println("LOGGER: msgLevel="+p+", msgType="+et+", "+message); + if (p == VCLogger.Priority.HighPriority) { + throw new VCLoggerException("Import failed : " + message); + } + } + @Override + public void sendAllMessages() { + } + @Override + public boolean hasMessages() { + return false; + } + }; + + + public static class VCellSedmlModel { + + } + + + public static void doit(File archiveFile) throws Exception{ + ArchiveComponents ac = null; + ac = Libsedml.readSEDMLArchive(new FileInputStream(archiveFile)); + + SEDMLDocument sedmlDoc = ac.getSedmlDocuments().get(0); + + SedML sedml = sedmlDoc.getSedMLModel(); + if(sedml == null || sedml.getModels().isEmpty()) { + throw new RuntimeException("sedml null or empty"); + } + + ModelResolver resolver = new ModelResolver(sedml); + // resolver.add(new FileModelResolver()); + resolver.add(new ArchiveModelResolver(ac)); + resolver.add(new BioModelsModelsRetriever()); + resolver.add(new URLResourceRetriever()); + // resolver.add(new RelativeFileModelResolver(FileUtils.getFullPath(archiveFile.getAbsolutePath()))); + + // + // iterate through all the elements and show them at the console + // + List mmm = sedml.getModels(); + for(Model mm : mmm) { + System.out.println(mm.toString()); + } + List sss = sedml.getSimulations(); + for(org.jlibsedml.Simulation ss : sss) { + System.out.println(ss.toString()); + } + List ttt = sedml.getTasks(); + for(AbstractTask tt : ttt) { + System.out.println(tt.toString()); + } + List ddd = sedml.getDataGenerators(); + for(DataGenerator dd : ddd) { + System.out.println(dd.toString()); + } + List ooo = sedml.getOutputs(); + for(Output oo : ooo) { + System.out.println(oo.toString()); + } + + // + // extract models referenced in tasks. + // + KisaoOntology kisaoInstance = KisaoOntology.getInstance(); +// HashMap flattenedModels = new HashMap(); + List taskList = sedml.getTasks(); + for (AbstractTask task : taskList){ + String modelReference = task.getModelReference(); + org.jlibsedml.Model sedmlOriginalModel = sedml.getModelWithId(modelReference); + + String sbmlModelString = resolver.getModelString(sedmlOriginalModel); + + XMLSource sbmlSource = new XMLSource(sbmlModelString); // sbmlSource with all the changes applied + + org.jlibsedml.Simulation sedmlSimulation = sedml.getSimulation(task.getSimulationReference()); + Algorithm algorithm = sedmlSimulation.getAlgorithm(); + KisaoTerm sedmlKisao = kisaoInstance.getTermById(algorithm.getKisaoID()); + + // + // try to find a VCell solverDescription to match the Kisao term + // + // UniformTimeCourse [initialTime=0.0, numberOfPoints=1000, outputEndTime=1.0, outputStartTime=0.0, + // Algorithm [kisaoID=KISAO:0000019], getId()=SimSlow] + + // identify the vCell solvers that would match best the sedml solver kisao id + List solverDescriptions = new ArrayList<>(); + for (SolverDescription sd : SolverDescription.values()) { + KisaoTerm solverKisaoTerm = kisaoInstance.getTermById(sd.getKisao()); + if(solverKisaoTerm == null) { + break; + } + boolean isExactlySame = solverKisaoTerm.equals(sedmlKisao); + if (isExactlySame && !solverKisaoTerm.isObsolete()) { + solverDescriptions.add(sd); // we make a list with all the solvers that match the kisao + } + } + if (solverDescriptions.isEmpty()){ + throw new RuntimeException("cannot find the solverDescription with exact match for Kisao term '"+sedmlKisao+"'"); + } + SolverDescription solverDescription = solverDescriptions.get(0); // choose first one + + // find out everything else we need about the application we're going to use, + // some of the info will be needed when we parse the sbml file + boolean bSpatial = false; + Application appType = Application.NETWORK_DETERMINISTIC; + Set sfList = solverDescription.getSupportedFeatures(); + for(SolverDescription.SolverFeature sf : sfList) { + switch(sf) { + case Feature_Rulebased: + if(appType != Application.SPRINGSALAD) { + // springs(alad) type takes precedence + appType = Application.RULE_BASED_STOCHASTIC; + } + break; + case Feature_Stochastic: + appType = Application.NETWORK_STOCHASTIC; + break; + case Feature_Deterministic: + appType = Application.NETWORK_DETERMINISTIC; + break; + case Feature_Springs: + appType = Application.SPRINGSALAD; + break; + case Feature_Spatial: + bSpatial = true; + break; + default: + break; + } + } + + BioModel bioModel = (BioModel)XmlHelper.importSBML(transLogger, sbmlSource, bSpatial); + + // + // we already have an application loaded from the sbml file, with initial conditions and stuff + // which may be not be suitable because the sedml kisao may need a different app type + // so we do a "copy as" to the right type and then delete the original we loaded from the sbml file + // + SimulationContext newSimulationContext = null; // the new application we're making from the old one + if(bioModel.getSimulationContexts().length == 1) { + SimulationContext oldSimulationContext = bioModel.getSimulationContext(0); + String newSCName = bioModel.getFreeSimulationContextName(); + newSimulationContext = SimulationContext.copySimulationContext(oldSimulationContext, newSCName, bSpatial, appType); + bioModel.addSimulationContext(newSimulationContext); + bioModel.removeSimulationContext(oldSimulationContext); + } else { + newSimulationContext = bioModel.addNewSimulationContext("App1", appType); + } + + // + // making the new vCell simulation based on the sedml simulation + // + newSimulationContext.refreshDependencies(); + MathMappingCallback callback = new MathMappingCallbackTaskAdapter(progressListener); + newSimulationContext.refreshMathDescription(callback, NetworkGenerationRequirements.ComputeFullStandardTimeout); + Simulation newSimulation = new Simulation(newSimulationContext.getMathDescription(), newSimulationContext); + newSimulation.setName(sedmlSimulation.getName()); + bioModel.addSimulation(newSimulation); + + // we identify the type of sedml simulation (uniform time course, etc) + // and set the vCell simulation parameters accordingly + if(sedmlSimulation instanceof UniformTimeCourse) { + + } else if(sedmlSimulation instanceof OneStep) { + + } else if(sedmlSimulation instanceof SteadyState) { + + } else { + + } + + System.out.println(XmlHelper.bioModelToXML(bioModel)); + } + + } + + + +// public void runSimulation(Simulation originalSimulation, File localSimDataDir){ +// Simulation simulation = new TempSimulation(originalSimulation, false); +// StdoutSessionLog log = new StdoutSessionLog("Quick run"); +// SimulationTask simTask = new SimulationTask(new SimulationJob(simulation, 0, null),0); +// Solver solver = createQuickRunSolver(log, localSimDataDir, simTask); +// if (solver == null) { +// throw new RuntimeException("null solver"); +// } +// // check if spatial stochastic simulation (smoldyn solver) has data processing instructions with field data - need to access server for field data, so cannot do local simulation run. +// if (solver instanceof SmoldynSolver) { +// DataProcessingInstructions dpi = simulation.getDataProcessingInstructions(); +// if (dpi != null) { +// FieldDataIdentifierSpec fdis = dpi.getSampleImageFieldData(simulation.getVersion().getOwner()); +// if (fdis != null) { +// throw new RuntimeException("Spatial Stochastic simulation '" + simulation.getName() + "' (Smoldyn solver) with field data (in data processing instructions) cannot be run locally at this time since field data needs to be retrieved from the VCell server."); +// } +// } +// } +// solver.addSolverListener(new SolverListener() { +// public void solverStopped(SolverEvent event) { +// getClientTaskStatusSupport().setMessage(event.getSimulationMessage().getDisplayMessage()); +// } +// public void solverStarting(SolverEvent event) { +// String displayMessage = event.getSimulationMessage().getDisplayMessage(); +// System.out.println(displayMessage); +// getClientTaskStatusSupport().setMessage(displayMessage); +// if(displayMessage.equals(SimulationMessage.MESSAGE_SOLVEREVENT_STARTING_INIT.getDisplayMessage())) { +// getClientTaskStatusSupport().setProgress(75); +// } else if(displayMessage.equals(SimulationMessage.MESSAGE_SOLVER_RUNNING_INPUT_FILE.getDisplayMessage())) { +// getClientTaskStatusSupport().setProgress(90); +// } +// } +// public void solverProgress(SolverEvent event) { +// getClientTaskStatusSupport().setMessage("Running..."); +// int progress = (int)(event.getProgress() * 100); +// getClientTaskStatusSupport().setProgress(progress); +// } +// public void solverPrinted(SolverEvent event) { +// getClientTaskStatusSupport().setMessage("Running..."); +// } +// public void solverFinished(SolverEvent event) { +// getClientTaskStatusSupport().setMessage(event.getSimulationMessage().getDisplayMessage()); +// } +// public void solverAborted(SolverEvent event) { +// getClientTaskStatusSupport().setMessage(event.getSimulationMessage().getDisplayMessage()); +// } +// }); +// solver.startSolver(); +// +// while (true){ +// try { +// Thread.sleep(500); +// } catch (InterruptedException e) { +// } +// +// if (getClientTaskStatusSupport().isInterrupted()) { +// solver.stopSolver(); +// throw UserCancelException.CANCEL_GENERIC; +// } +// SolverStatus solverStatus = solver.getSolverStatus(); +// if (solverStatus != null) { +// if (solverStatus.getStatus() == SolverStatus.SOLVER_ABORTED) { +// throw new RuntimeException(solverStatus.getSimulationMessage().getDisplayMessage()); +// } +// if (solverStatus.getStatus() != SolverStatus.SOLVER_STARTING && +// solverStatus.getStatus() != SolverStatus.SOLVER_READY && +// solverStatus.getStatus() != SolverStatus.SOLVER_RUNNING){ +// break; +// } +// } +// } +// +// ArrayList outputFunctionsList = getSimWorkspace().getSimulationOwner().getOutputFunctionContext().getOutputFunctionsList(); +// OutputContext outputContext = new OutputContext(outputFunctionsList.toArray(new AnnotatedFunction[outputFunctionsList.size()])); +// Simulation[] simsArray = new Simulation[] {simulation}; +// hashTable.put("outputContext", outputContext); +// hashTable.put("simsArray", simsArray); +// +// } + + + public static void main(String[] args) { + try { + doit(new File("C:\\temp\\fff\\BBasicModel.sedx")); + }catch (Exception e){ + e.printStackTrace(System.out); + }finally{ + System.exit(0); + } + + } + +} diff --git a/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_model_bad.vcml b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_model_bad.vcml new file mode 100644 index 0000000000..c344e00bbd --- /dev/null +++ b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_model_bad.vcml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_reactions_bad.vcml b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_reactions_bad.vcml new file mode 100644 index 0000000000..802af546e2 --- /dev/null +++ b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_reactions_bad.vcml @@ -0,0 +1,1010 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.4 + 0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.5 + 0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.7 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.6 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.3 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.7 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.7 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.5 + 0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.5 + 0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Mixed binding / transitoning reactions are not permitted. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Transition bound reactions always show, by convention, 2 molecules. One + depicts the binding condition, the other depicts the state transition. + Here, the binding molecule is missing. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Mixed binding / transitoning reactions are not permitted. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + In a Decay reaction, the product must be the reserved molecule 'Sink' + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Binding cannot be defined within the same molecule. Such a connection, + if exist, must be defined as an internal Link. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Transition bound reactions always show, by convention, 2 molecules. One + depicts the binding condition, the other depicts the state transition. + Here, the binding condition is missing. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + The non-transitioning sites must be, by convention, marked as bond + 'Possible' and state 'Any' + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + In a Creation reaction, the reactant must be the reserved molecule + 'Source' + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Only one site with allosteric state condition is allowed + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Binding cannot be defined within the same molecule. Such a connection, + if exist, must be defined as an internal Link. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Transition bound reactions always show, by convention, 2 molecules. One + depicts the binding condition, the other depicts the state transition. + Here, the binding condition is missing. + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Only one binding is allowed per Binding reaction + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Transition bound reactions always show, by convention, 2 molecules. One + depicts the binding condition, the other depicts the state transition. + Here, binding condition and the state transition are depicted on the + same molecule + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + The non-transitioning sites must be, by convention, marked as bond + 'Unbound' and state 'Any' + </p> + </body> +</html> + + + + + + + diff --git a/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_reactions_good.vcml b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_reactions_good.vcml new file mode 100644 index 0000000000..44661e3acd --- /dev/null +++ b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_reactions_good.vcml @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + 1.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.4 + 0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.5 + 0.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.7 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.6 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.3 + 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Note that there is no conflict here, the meaning is that there must + really be 2 molecules of the same tipe, one with the binding condition + site in state 1, the other one transitioning from state 0 to state 1 + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + For transition bound reaction, we separately must show the binding + condition molecule and the transition molecule (as a convention) even + when it actually is the same molecule + </p> + <p style="margin-top: 0"> + The reason is that it may result in a confusion with allosteric + representation + </p> + </body> +</html> + + + <html> + <head> + + </head> + <body> + <p style="margin-top: 0"> + Transition bound reactions always show, by convention, 2 molecules. One + depicts the binding condition, the other depicts the state transition + </p> + </body> +</html> + + + + + + + diff --git a/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_transition_free.vcml b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_transition_free.vcml new file mode 100644 index 0000000000..d04afadda5 --- /dev/null +++ b/vcell-core/src/test/resources/cbit/vcell/biomodel/Spring_transition_free.vcml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 50.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vcell-oracle/pom.xml b/vcell-oracle/pom.xml index 4ab21c9d6a..d26564b758 100644 --- a/vcell-oracle/pom.xml +++ b/vcell-oracle/pom.xml @@ -123,17 +123,5 @@ 42.4.1 - - com.oracle - ojdbc6 - 11.2.0.4 - - - - com.oracle - ucp - 11.2.0.4 - - diff --git a/vcell-oracle/src/main/java/org/vcell/db/oracle/OraclePoolingConnectionFactory.java b/vcell-oracle/src/main/java/org/vcell/db/oracle/OraclePoolingConnectionFactory.java index 1da798c421..2e4e2ed9b5 100644 --- a/vcell-oracle/src/main/java/org/vcell/db/oracle/OraclePoolingConnectionFactory.java +++ b/vcell-oracle/src/main/java/org/vcell/db/oracle/OraclePoolingConnectionFactory.java @@ -100,6 +100,7 @@ public synchronized Connection getConnection(Object lock) throws SQLException { "3. there is a problem with network.\n" + "details: "+detailedErrorMsg, exception); } + conn.setAutoCommit(false); return conn; } diff --git a/vcell-server/pom.xml b/vcell-server/pom.xml index 7b6c84c33a..77d748d5bf 100644 --- a/vcell-server/pom.xml +++ b/vcell-server/pom.xml @@ -76,14 +76,34 @@ UConn Health + + + + com.oracle.database.jdbc + ojdbc-bom + ${ojdbc-bom.version} + pom + import + + + + + + com.oracle.database.jdbc + ojdbc8 + + + com.oracle.database.jdbc + ucp + + junit junit test - org.vcell vcell-core diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/BioModelTable.java b/vcell-server/src/main/java/cbit/vcell/modeldb/BioModelTable.java index 92600a5591..1743fd360a 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/BioModelTable.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/BioModelTable.java @@ -177,8 +177,8 @@ public String getPreparedStatement_BioModelReps(String conditions, OrderBy order GroupTable groupTable = GroupTable.table; UserTable userTable = UserTable.table; - String concat_function_name = (dbSyntax==DatabaseSyntax.ORACLE) ? "wm_concat" : "string_agg"; - String concat_second_arg = (dbSyntax==DatabaseSyntax.ORACLE) ? "" : ", ','"; + String concat_function_name = (dbSyntax==DatabaseSyntax.ORACLE) ? "listagg" : "string_agg"; + String concat_second_arg = (dbSyntax==DatabaseSyntax.ORACLE) ? ", ','" : ", ','"; String string_cast = (dbSyntax==DatabaseSyntax.ORACLE) ? "" : "::varchar(255)"; String subquery = diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/DBBackupAndClean.java b/vcell-server/src/main/java/cbit/vcell/modeldb/DBBackupAndClean.java index 43c52da1b8..f01e03fd2f 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/DBBackupAndClean.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/DBBackupAndClean.java @@ -146,7 +146,7 @@ private static void usageExit(){ System.out.println(DBBackupAndClean.class.getName()+" "+OP_CLEAN_AND_BACKUP+" dbHostName vcellSchema password dbSrvcName workingDir exportDir {amplistorUser amplistorPasswd}"); System.out.println(DBBackupAndClean.class.getName()+" "+OP_IMPORT+" importServerName dumpFileHostPrefix vcellSchema password importSrvcName workingDir exportDir"); System.out.println(DBBackupAndClean.class.getName()+" "+OP_DELSIMSDISK2+ " dbHostName vcellSchema password/pwdfile dbSrvcName tmpDir usersDir {thisUserOnly}"); - System.out.println(" (exmpl: "+DBBackupAndClean.class.getName()+" "+OP_DELSIMSDISK2+" vcell-db vcell vcpassword/pwdfile vcelldborcl.cam.uchc.edu /tmp /share/apps/vcell3/users"+")"); + System.out.println(" (exmpl: "+DBBackupAndClean.class.getName()+" "+OP_DELSIMSDISK2+" vcell-oracle vcell vcpassword/pwdfile ORCLPDB1 /tmp /share/apps/vcell3/users"+")"); System.exit(1); } diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/DbDriver.java b/vcell-server/src/main/java/cbit/vcell/modeldb/DbDriver.java index ed871f38f9..ec39537353 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/DbDriver.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/DbDriver.java @@ -1101,6 +1101,8 @@ protected final static Object getLOB(ResultSet rset,Field column,DatabaseSyntax return blob_object.getBytes((long) 1, (int) blob_object.length()); }catch(Exception e){ throw new DataAccessException(e.toString()); + }finally { + blob_object.free(); } //If its a CLOB return a String } else @@ -1111,6 +1113,8 @@ protected final static Object getLOB(ResultSet rset,Field column,DatabaseSyntax clob_object.getAsciiStream().read(ins); } catch (Exception e) { throw new DataAccessException(e.toString()); + }finally { + clob_object.free(); } return new String(ins); } diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/GeomDbDriver.java b/vcell-server/src/main/java/cbit/vcell/modeldb/GeomDbDriver.java index b466b87ad0..429c449d84 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/GeomDbDriver.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/GeomDbDriver.java @@ -1255,7 +1255,7 @@ private void insertVCImage(InsertHashtable hash, Connection con, User user,VCIma insertBrowseImageDataSQL(con, browseImageKey, newVersion.getVersionKey()/*imageKey*/, image); }catch(cbit.image.GifParsingException e){ lg.error(e.getMessage(),e); - throw new DataAccessException("Error Parsing BrowseImage",e); + throw new DataAccessException("GitParsingException while storing BrowseImage", e); } VCPixelClass vcPixelClasses[] = image.getPixelClasses(); for (int i = 0; i < vcPixelClasses.length; i++){ diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/ImageTable.java b/vcell-server/src/main/java/cbit/vcell/modeldb/ImageTable.java index 81b5f51c92..5b1031fcc7 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/ImageTable.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/ImageTable.java @@ -9,6 +9,7 @@ */ package cbit.vcell.modeldb; +import java.math.BigDecimal; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; @@ -90,7 +91,10 @@ public VersionInfo getInfo(ResultSet rset, Connection con,DatabaseSyntax dbSynta gifImage = new GIFImage(gifData); // }catch (GifParsingException e){ - throw new DataAccessException("Error Parsing browseImage"); + BigDecimal imageKeyValue = rset.getBigDecimal(BrowseImageDataTable.table.imageRef.getQualifiedColName()); + String msg = "GifParsingException while parsing browseImage for vc_image(key="+imageKeyValue+"): "+e.getMessage(); + lg.error(msg, e); + throw new DataAccessException(msg, e); } java.math.BigDecimal groupid = rset.getBigDecimal(VersionTable.privacy_ColumnName); diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/PublicationTable.java b/vcell-server/src/main/java/cbit/vcell/modeldb/PublicationTable.java index 5813767c36..fd44e7e5ab 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/PublicationTable.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/PublicationTable.java @@ -87,8 +87,8 @@ public String getPreparedStatement_PublicationReps(String conditions, OrderBy or PublicationTable pubTable = PublicationTable.table; PublicationModelLinkTable pubModelTable = PublicationModelLinkTable.table; - String concat_function_name = (dbSyntax==DatabaseSyntax.ORACLE) ? "wm_concat" : "string_agg"; - String concat_second_arg = (dbSyntax==DatabaseSyntax.ORACLE) ? "" : ", ','"; + String concat_function_name = (dbSyntax==DatabaseSyntax.ORACLE) ? "listagg" : "string_agg"; + String concat_second_arg = (dbSyntax==DatabaseSyntax.ORACLE) ? ", ','" : ", ','"; String string_cast = (dbSyntax==DatabaseSyntax.ORACLE) ? "" : "::varchar(255)"; String subquery = diff --git a/vcell-server/src/main/java/cbit/vcell/modeldb/SimContextTable.java b/vcell-server/src/main/java/cbit/vcell/modeldb/SimContextTable.java index 0fae894f50..199bbca316 100644 --- a/vcell-server/src/main/java/cbit/vcell/modeldb/SimContextTable.java +++ b/vcell-server/src/main/java/cbit/vcell/modeldb/SimContextTable.java @@ -315,7 +315,7 @@ public static String getAppComponentsForDatabase(SimulationContext simContext) { // ReactionRuleSpecs ReactionRuleSpec[] reactionRuleSpecs = simContext.getReactionContext().getReactionRuleSpecs(); if (reactionRuleSpecs != null && reactionRuleSpecs.length > 0){ - Element reactionRuleSpecsElement = xmlProducer.getXML(reactionRuleSpecs); + Element reactionRuleSpecsElement = xmlProducer.getXML(reactionRuleSpecs, simContext); appComponentsElement.addContent(reactionRuleSpecsElement); } diff --git a/vcell-server/src/main/java/org/vcell/db/migrate/MigrateDbManager.java b/vcell-server/src/main/java/org/vcell/db/migrate/MigrateDbManager.java index f3189fbbdb..7205b3464d 100644 --- a/vcell-server/src/main/java/org/vcell/db/migrate/MigrateDbManager.java +++ b/vcell-server/src/main/java/org/vcell/db/migrate/MigrateDbManager.java @@ -29,7 +29,7 @@ public static void main(String[] args) throws SQLException { try { String driverName_Oracle = "oracle.jdbc.driver.OracleDriver"; - String connectionURL_Oracle = "jdbc:oracle:thin:@VCELL-DB.cam.uchc.edu:1521/vcelldborcl.cam.uchc.edu"; + String connectionURL_Oracle = "jdbc:oracle:thin:@vcell-oracle.cam.uchc.edu:1521/ORCLPDB1"; String userid_Oracle = "vcell"; String password_Oracle = null; conFactory_Oracle = DatabaseService.getInstance().createConnectionFactory(driverName_Oracle,connectionURL_Oracle,userid_Oracle,password_Oracle); @@ -179,10 +179,14 @@ public static void migrate(Table table, ConnectionFactory conFactory_from, Conne Object lob_object = rset_from.getObject(i+1); if(!rset_from.wasNull()){ if (lob_object instanceof java.sql.Blob) { - java.sql.Blob blob_object = (java.sql.Blob) lob_object; - byte[] bytes = blob_object.getBytes((long) 1, (int) blob_object.length()); - stmt_to.setBytes(i+1, bytes); - found = true; + try { + java.sql.Blob blob_object = (java.sql.Blob) lob_object; + byte[] bytes = blob_object.getBytes((long) 1, (int) blob_object.length()); + stmt_to.setBytes(i + 1, bytes); + found = true; + } finally { + ((java.sql.Blob) lob_object).free(); + } } } if (!found){ @@ -204,6 +208,8 @@ public static void migrate(Table table, ConnectionFactory conFactory_from, Conne found = true; } catch (IOException e) { e.printStackTrace(); + } finally { + clob_object.free(); } } } diff --git a/vcell-server/src/main/java/org/vcell/db/postgres/PostgresConnectionFactory.java b/vcell-server/src/main/java/org/vcell/db/postgres/PostgresConnectionFactory.java index b87f24f6d3..4bf64b1589 100644 --- a/vcell-server/src/main/java/org/vcell/db/postgres/PostgresConnectionFactory.java +++ b/vcell-server/src/main/java/org/vcell/db/postgres/PostgresConnectionFactory.java @@ -58,7 +58,6 @@ public synchronized Connection getConnection(Object lock) throws SQLException { Connection conn = null; try { conn = poolDataSource.getConnection(); - conn.setAutoCommit(false); } catch (SQLException ex) { // might be invalid or stale connection if (lg.isTraceEnabled()) lg.trace("first time #getConnection( ) fail " + ex.getMessage() + ", state " + ex.getSQLState() + ", attempting refresh"); @@ -76,6 +75,7 @@ public synchronized Connection getConnection(Object lock) throws SQLException { "2. there is a problem with database server.\n" + "3. there is a problem with network.\n"); } + conn.setAutoCommit(false); return conn; } diff --git a/vcell-util/src/main/java/org/vcell/util/IssueContext.java b/vcell-util/src/main/java/org/vcell/util/IssueContext.java index b4b7839579..032b10394e 100644 --- a/vcell-util/src/main/java/org/vcell/util/IssueContext.java +++ b/vcell-util/src/main/java/org/vcell/util/IssueContext.java @@ -30,6 +30,8 @@ public enum ContextType { RbmObservable, SpeciesContext, // context for species pattern MolecularType, + MolecularInternalLinkSpec, // context for SpringSaLaD-specific objects + SiteAttributesSpec, } private final IssueContext.ContextType contextType; diff --git a/vcell-util/src/main/java/org/vcell/util/Pair.java b/vcell-util/src/main/java/org/vcell/util/Pair.java index 6beef2cfb3..5e8ba3619e 100644 --- a/vcell-util/src/main/java/org/vcell/util/Pair.java +++ b/vcell-util/src/main/java/org/vcell/util/Pair.java @@ -13,8 +13,9 @@ */ package org.vcell.util; +import java.io.Serializable; -public class Pair { +public class Pair implements Serializable { public final One one; public final Two two; diff --git a/vcell-vmicro/src/main/java/org/vcell/vmicro/op/Generate2DExpModelOpAbstract.java b/vcell-vmicro/src/main/java/org/vcell/vmicro/op/Generate2DExpModelOpAbstract.java index 8b1ac0dc59..92b5316223 100644 --- a/vcell-vmicro/src/main/java/org/vcell/vmicro/op/Generate2DExpModelOpAbstract.java +++ b/vcell-vmicro/src/main/java/org/vcell/vmicro/op/Generate2DExpModelOpAbstract.java @@ -21,6 +21,7 @@ import cbit.vcell.geometry.GeometryException; import cbit.vcell.geometry.SubVolume; import cbit.vcell.mapping.FeatureMapping; +import cbit.vcell.mapping.IllegalMappingException; import cbit.vcell.mapping.MappingException; import cbit.vcell.mapping.MathMapping; import cbit.vcell.mapping.MicroscopeMeasurement.GaussianConvolutionKernel; @@ -72,7 +73,7 @@ public final GeneratedModelResults generateModel( double secondaryFraction, String extracellularName, String cytosolName, - Context context) throws PropertyVetoException, ExpressionException, GeometryException, ImageException, ModelException, MappingException, MathException, MatrixException { + Context context) throws PropertyVetoException, ExpressionException, GeometryException, ImageException, ModelException, MappingException, MathException, MatrixException, IllegalMappingException { double domainSize = 2.2*cellRadius; Extent extent = new Extent(domainSize, domainSize, 1.0);