OSDN Git Service

Start implementing JANPA integration with Psi4
authorToshi Nagata <alchemist.2005@nifty.com>
Sun, 2 Oct 2022 14:52:47 +0000 (23:52 +0900)
committerToshi Nagata <alchemist.2005@nifty.com>
Sun, 2 Oct 2022 14:52:47 +0000 (23:52 +0900)
.gitignore
JANPA/License.txt [new file with mode: 0644]
Makefile
MolLib/Ruby_bind/ruby_bind.c
Scripts/commands.rb
Scripts/gamess.rb
Scripts/loadsave.rb
build-xcode/Molby.xcodeproj/project.pbxproj
wxSources/MyApp.cpp

index 2d44466..840b992 100644 (file)
@@ -5,3 +5,5 @@ memo.txt
 revisionInfo.txt
 latest_binaries
 Documents/MolbyDoc/
+JANPA/*.jar
+
diff --git a/JANPA/License.txt b/JANPA/License.txt
new file mode 100644 (file)
index 0000000..dbd9301
--- /dev/null
@@ -0,0 +1,45 @@
+Software License for JANPA package of programs
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+All advertising and/or published materials mentioning features or use
+of this software must display the following acknowledgement:
+
+This product includes components from JANPA package of programs
+( http://janpa.sourceforge.net/) developed by Tymofii Nikolaienko
+
+Neither the name of the developer, Tymofii Nikolaienko, nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+In case if any results obtained with JANPA package of programs are
+published, the following citations for the JANPA package of programs
+should be given:
+
+1) T. Yu. Nikolaienko, L. A. Bulavin; Localized orbitals for optimal
+decomposition of molecular properties, Int. J. Quantum Chem. (2019),
+Vol. 119, page e25798, DOI: 10.1002/qua.25798
+2) T.Y.Nikolaienko, L.A.Bulavin, D.M.Hovorun; JANPA: an open source
+cross-platform implementation of the Natural Population Analysis on the
+Java platform, Comput.Theor.Chem.(2014), V.1050, P.15-22,
+DOI: 10.1016/j.comptc.2014.10.002 , http://janpa.sourceforge.net
+
+THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL Tymofii Nikolaienko BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
index a4cf5b2..fa85e1c 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -165,6 +165,7 @@ ifeq ($(TARGET_PLATFORM),MSW)
        cp -r ../bitmaps/bitmaps $(DESTPREFIX)/$(PRODUCT_DIR)
        cp -r amber11 $(DESTPREFIX)/$(PRODUCT_DIR)
        cp -r ortep3 $(DESTPREFIX)/$(PRODUCT_DIR)
+       cp -r ../JANPA $(DESTPREFIX)/$(PRODUCT_DIR)
        cp -r ../Documents/MolbyDoc $(DESTPREFIX)/$(PRODUCT_DIR)
        mkdir -p $(DESTPREFIX)/$(PRODUCT_DIR)/Scripts/lib
        for i in $(RUBY_EXTLIB); do cp $(RUBY_DIR)/lib/$$i $(DESTPREFIX)/$(PRODUCT_DIR)/Scripts/lib; done
index 520889d..9d7452e 100644 (file)
@@ -1012,6 +1012,7 @@ s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
     VALUE save_interruptFlag;
        int n, exitstatus, pid;
        char *sout, *serr;
+    const char *pnamestr;
        FILE *fpout, *fperr;
 
        rb_scan_args(argc, argv, "23", &cmd, &procname, &cproc, &stdout_val, &stderr_val);
@@ -1056,7 +1057,10 @@ s_Kernel_CallSubProcess(int argc, VALUE *argv, VALUE self)
        }
     
     save_interruptFlag = s_SetInterruptFlag(self, Qnil);
-       n = MyAppCallback_callSubProcess(StringValuePtr(cmd), StringValuePtr(procname), (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
+    if (procname != Qnil)
+        pnamestr = StringValuePtr(procname);
+    else pnamestr = NULL;
+       n = MyAppCallback_callSubProcess(StringValuePtr(cmd), pnamestr, (cproc == Qnil ? NULL : s_Kernel_CallSubProcess_Callback), (cproc == Qnil ? NULL : (void *)cproc), fpout, fperr, &exitstatus, &pid);
     s_SetInterruptFlag(self, save_interruptFlag);
     
        if (fpout != NULL && fpout != (FILE *)1)
index ee4c7ab..8ce11cd 100755 (executable)
@@ -395,6 +395,14 @@ class Molecule
                          name2 = "Pre-orthogonal Natural Hybrid Orbitals"
                        elsif key2 == "PNBO"
                          name2 = "Pre-orthogonal Natural Bond Orbitals"
+      elsif key2 == "AHO"
+        name2 = "Atomic Hybrid Orbitals"
+      elsif key2 == "LHO"
+        name2 = "Localized Hybrid Orbitals"
+      elsif key2 == "LPO"
+        name2 = "Localized Property-optimized Orbitals"
+      elsif key2 == "CLPO"
+        name2 = "Chemist's Localized Property-optimized Orbitals"
                        end
                        mo_ao_items.push(name2)
                        mo_ao_keys.push(key2)
index 560082a..66978ab 100755 (executable)
@@ -1553,6 +1553,7 @@ class Molecule
       fp.print "#  Generated by Molby at #{now}\n"
       fp.print "import sys\n"
       fp.print "import re\n"
+      fp.print "base = re.sub('\\.\\w*$', '', sys.argv[1])  #  Basename of the input file\n"
       fp.print "molecule mol {\n"
       charge = Integer(hash["charge"])
       mult = Integer(hash["mult"])
@@ -1584,7 +1585,17 @@ class Molecule
       end
       runtype = hash['runtype'].downcase
       fp.print "energy, wfn = #{runtype}('scf', #{options})\n"
-      fp.print "wfn.write_molden(re.sub('\\.\\w*$', '.molden', sys.argv[1]))\n"
+      fp.print "wfn.write_molden(f'{base}.molden')\n"
+      if hash['run_junpa'] != 0
+        fp.print "\n"
+        fp.print "#  Interface for JANPA\n"
+        fp.print "#  cf. https://sourceforge.net/p/janpa/wiki/psi4Examples/\n"
+        fp.print "d = wfn.Da().to_array()\n"
+        fp.print "s = wfn.S().to_array()\n"
+        fp.print "c = wfn.Ca().to_array()\n"
+        fp.print "occs = c.T.dot(s.dot(d).dot(s).dot(c))\n"
+        fp.print "molden(wfn, f'{base}.da.molden', density_a = psi4.core.Matrix.from_array(occs))\n"
+      end
     end
     if fname == nil
       s = fp.buffer
@@ -1596,6 +1607,42 @@ class Molecule
     end
   end
 
+  #  Execute JUNPA
+  #  inppath is the input file minus extention
+  #  mol is the molecule (may be nil)
+  def Molecule.execute_janpa(inppath, mol, spherical)
+    #    nbo_desc =   #  JANPA
+    janpa_dir = "#{ResourcePath}/JANPA"
+    status = 0
+    if spherical
+      cmd1 = "java -jar #{janpa_dir}/molden2molden.jar -NormalizeBF -i #{inppath}.da.molden -o #{inppath}.in.molden >#{inppath}.janpa.log"
+    else
+    cmd1 = "java -jar #{janpa_dir}/molden2molden.jar -frompsi4v1mo -NormalizeBF -cart2pure -i #{inppath}.da.molden -o #{inppath}.in.molden >#{inppath}.janpa.log"
+    end
+    flag = system(cmd1)
+    if flag
+      cmd2 = "java -jar #{janpa_dir}/janpa.jar -i #{inppath}.in.molden"
+      ["nao", "pnao", "aho", "lho", "lpo", "clpo"].each { |type|
+        if (get_global_settings("psi4.#{type}").to_i != 0)
+          cmd2 += " -#{type.upcase}_Molden_File #{inppath}.#{type.upcase}.molden"
+        end
+      }
+      cmd2 += " >>#{inppath}.janpa.log"
+      flag = system(cmd2)
+    end
+    if flag
+      if mol
+        #  import JANPA log and molden output
+        #  Files: inppath.japa.log, inppath.{NAO,PNAO,AHO,LHO,LPO,CLPO}.molden
+        mol.sub_load_janpa_log(inppath)
+      end
+    else
+      status = $?.exitstatus
+      message_box("Execution of #{procname} failed with status #{status}.", "JANPA Failed")
+    end
+    return status
+  end
+  
   #  Execute Psi4
   #  inpname is the input file
   #  mol (optional) is the molecule from which the Psi4 input was built.
@@ -1623,6 +1670,7 @@ class Molecule
     hf_type = nil
     nalpha = nil
     nbeta = nil
+    spherical = false
 
     outfile = inpdir + "/" + inpbody + ".out"
     if File.exists?(outfile)
@@ -1731,6 +1779,8 @@ class Molecule
             nalpha = Integer($1)
           elsif line =~ /^ *Nbeta *= *(\d+)/
             nbeta = Integer($1)
+          elsif line =~ /^ *Spherical Harmonics\?: *(\w+)/
+            spherical = ($1 == "true")
           end
         end
         if next_index > 0
@@ -1748,6 +1798,7 @@ class Molecule
 
     #  Terminate callback
     term_callback = lambda { |m, n|
+      do_janpa = false
       begin
         msg = "Psi4 execution of #{inpbase} "
         hmsg = "Psi4 "
@@ -1793,6 +1844,9 @@ class Molecule
               $stderr.write("#{e.backtrace.inspect}\n")
             end
           end
+          if (get_global_settings("psi4.run_janpa").to_i == 1)
+            do_janpa = true
+          end
         elsif n == -1
           #  The child process actually did not start
           #  Restore the old file if outbackfile is not nil
@@ -1803,6 +1857,9 @@ class Molecule
         if outbackfile && File.exists?(outbackfile)
           File.delete(outbackfile)
         end
+        if do_janpa
+          Molecule.execute_janpa(inpdir + "/" + inpbody, mol, spherical)
+        end
       rescue => e
         $stderr.write("#{e.message}\n")
         $stderr.write("#{e.backtrace.inspect}\n")
@@ -1853,7 +1910,7 @@ class Molecule
     dfttype_desc = ["B3LYP"]
     runtype_desc = ["Energy", "Optimize"]
     scftype_desc = ["RHF", "ROHF", "UHF"]
-#   nbo_desc = ["nao", "nbo", "nho", "nlmo", "pnao", "pnbo", "pnho", "pnlmo"]
+    nbo_desc = ["nao", "pnao", "aho", "lho", "lpo", "clpo"]  #  JANPA
     user_input = Hash.new
     defaults = {"scftype"=>0, "runtype"=>0, "move_to_com"=>0, "do_reorient"=>0,
       "use_symmetry"=>0,  "charge"=>"0", "mult"=>"1",
@@ -1956,22 +2013,21 @@ class Molecule
         #item(:line),
         #-1, -1, -1,
         #  ------
-        #item(:checkbox, :title=>"Include NBO instructions", :tag=>"include_nbo",
-        #    :action=>lambda { |it|
-        #      flag = (it[:value] != 0)
-        #      nbos.each { |nbo| set_attr(nbo, :enabled=>flag) }
-        #    }),
-        #-1, -1, -1,
+        item(:checkbox, :title=>"Run JANPA after Psi4", :tag=>"run_janpa",
+            :action=>lambda { |it|
+              flag = (it[:value] != 0)
+              nbo_desc.each { |nbo| set_attr(nbo, :enabled=>flag) }
+            }),
+        -1, -1, -1,
         #  ------
-        #item(:checkbox, :title=>"NAO", :tag=>"nao"),
-        #item(:checkbox, :title=>"NBO", :tag=>"nbo"),
-        #item(:checkbox, :title=>"NHO", :tag=>"nho"),
-        #item(:checkbox, :title=>"NLMO", :tag=>"nlmo"),
+        item(:checkbox, :title=>"NAO", :tag=>"nao"),
+        item(:checkbox, :title=>"PNAO", :tag=>"pnao"),
+        item(:checkbox, :title=>"AHO", :tag=>"aho"),
+        item(:checkbox, :title=>"LHO", :tag=>"lho"),
         #  ------
-        #item(:checkbox, :title=>"PNAO", :tag=>"pnao"),
-        #item(:checkbox, :title=>"PNBO", :tag=>"pnbo"),
-        #item(:checkbox, :title=>"PNHO", :tag=>"pnho"),
-        #item(:checkbox, :title=>"PNLMO", :tag=>"pnlmo"),
+        item(:checkbox, :title=>"LPO", :tag=>"lpo"),
+        item(:checkbox, :title=>"CLPO", :tag=>"clpo"),
+        -1, -1,
         #  ------
         item(:line),
         -1, -1, -1,
index 888224d..b6735b4 100755 (executable)
@@ -611,13 +611,22 @@ class Molecule
   end
 
   #  mol.set_mo_info should be set before calling this function
-  def sub_load_molden(fp)
+  #  Optional label is for importing JANPA output: "NAO" or "CPLO"
+  #  If label is not nil, then returns a hash containing the following key/value pairs:
+  #    :atoms => an array of [element_symbol, seq_num, atomic_num, x, y, z] (angstrom)
+  #    :gto => an array of an array of [sym, [ex0, c0, ex1, c1, ...]]
+  #    :moinfo => an array of [sym, energy, spin (0 or 1), occ]
+  #    :mo => an array of [c0, c1, ...]
+  def sub_load_molden(fp, label = nil)
     getline = lambda { @lineno += 1; return fp.gets }
     bohr = 0.529177210903
     errmsg = nil
     ncomps = 0  #  Number of components (AOs)
     occ_alpha = 0  #  Number of occupied alpha orbitals
     occ_beta = 0   #  Number of occupied beta orbitals
+    if label
+      hash = Hash.new
+    end
     catch :ignore do
       while line = getline.call
         if line =~ /^\[Atoms\]/
@@ -626,12 +635,16 @@ class Molecule
             if line =~ /^[A-Z]/
               #  element, index, atomic_number, x, y, z (in AU)
               a = line.split(' ')
-              if atoms[i].atomic_number != Integer(a[2]) ||
-                (atoms[i].x - Float(a[3]) * bohr).abs > 1e-4 ||
-                (atoms[i].y - Float(a[4]) * bohr).abs > 1e-4 ||
-                (atoms[i].z - Float(a[5]) * bohr).abs > 1e-4
-                errmsg = "The atom list does not match the current molecule."
-                throw :ignore
+              if label
+                (hash[:atoms] ||= []).push([a[0], Integer(a[1]), Integer(a[2]), Float(a[3]) * bohr, Float(a[4]) * bohr, Float(a[5]) * bohr])
+              else
+                if atoms[i].atomic_number != Integer(a[2]) ||
+                  (atoms[i].x - Float(a[3]) * bohr).abs > 1e-4 ||
+                  (atoms[i].y - Float(a[4]) * bohr).abs > 1e-4 ||
+                  (atoms[i].z - Float(a[5]) * bohr).abs > 1e-4
+                  errmsg = "The atom list does not match the current molecule."
+                  throw :ignore
+                end
               end
               i += 1
             else
@@ -642,6 +655,10 @@ class Molecule
         elsif line =~ /^\[GTO\]/
           shell = 0
           atom_index = 0
+          if label
+            gtos = []
+            hash[:gto] = []
+          end
           while line = getline.call
             #  index, 0?
             a = line.split(' ')
@@ -666,24 +683,40 @@ class Molecule
                 raise MolbyError, "Unknown gaussian shell type '#{a[0]}' at line #{@lineno} in MOLDEN file"
               end
               nprimitives = Integer(a[1])
+              if label
+                gtoline = [sym, []]
+                gtos.push(gtoline)
+              end
               nprimitives.times { |i|
                 line = getline.call   #  exponent, contraction
                 b = line.split(' ')
+                if label
+                  gtoline[1].push(Float(b[0]), Float(b[1]))
+                end
                 add_gaussian_primitive_coefficients(Float(b[0]), Float(b[1]), 0.0)
               }
               #  end of one shell
-              add_gaussian_orbital_shell(atom_index, sym, nprimitives)
+              if label == nil
+                add_gaussian_orbital_shell(atom_index, sym, nprimitives)
+              end
               shell += 1
               ncomps += n
             end
             #  end of one atom
             atom_index += 1
+            if label
+              hash[:gto].push(gtos)
+            end
           end
           redo  #  The next line will be the beginning of the next block
         elsif line =~ /^\[MO\]/
           m = []
           idx_alpha = 1   #  set_mo_coefficients() accepts 1-based index of MO
           idx_beta = 1
+          if label
+            hash[:mo] = []
+            hash[:moinfo] = []
+          end
           while true
             #  Loop for each MO
             m.clear
@@ -708,6 +741,9 @@ class Molecule
                     occ_beta += 1
                   end
                 end
+                if label
+                  hash[:moinfo].push([sym, ene, (spin == "Alpha" ? 0 : 1), occ])
+                end
               elsif line =~ /^ *([0-9]+) +([-+.0-9e]+)/
                 m[i] = Float($2)
                 i += 1
@@ -720,6 +756,9 @@ class Molecule
                     idx_beta += 1
                   end
                   set_mo_coefficients(idx, ene, m)
+                  if label
+                    hash[:mo].push(m.dup)
+                  end
                   break
                 end
               else
@@ -734,11 +773,96 @@ class Molecule
     end     #  end catch
     if errmsg
       message_box("The MOLDEN file was found but not imported. " + errmsg, "Psi4 import info", :ok)
-      return false
+      return (label ? nil : false)
     end
-    return true
+    return (label ? hash : true)
   end
-  
+
+  #  Import the JANPA log and related molden files
+  #  Files: inppath.{NAO.molden,CLPO.molden,janpa.log}
+  def sub_load_janpa_log(inppath)
+    begin
+      fp = File.open(inppath + ".janpa.log", "rt") rescue fp = nil
+      if fp == nil
+        message_box("Cannot open JANPA log file #{inppath + '.janpa.log'}: " + $!.to_s)
+        return false
+      end
+      print("Importing #{inppath}.janpa.log.\n")
+      lineno = 0
+      getline = lambda { lineno += 1; return fp.gets }
+      h = Hash.new
+      mfiles = Hash.new
+      while line = getline.call
+        if line =~ /^NAO \#/
+          h["NAO"] = []
+          while line = getline.call
+            break if line !~ /^\s*[1-9]/
+            num = Integer(line[0, 5])
+            name = line[5, 21]
+            occ = Float(line[26, 11])
+            #  like A1*: R1*s(0)
+            #  atom_number, occupied?, shell_number, orb_sym, angular_number
+            name =~ /\s*[A-Z]+([0-9]+)(\*?):\s* R([0-9]+)\*([a-z]+)\(([-0-9]+)\)/
+            anum = Integer($1)
+            occupied = $2
+            shell_num = Integer($3)
+            orb_sym = $4
+            ang_num = Integer($5)
+            h["NAO"].push([num, anum, occupied, shell_num, orb_sym, ang_num, occ])
+          end
+        elsif line =~ /^\s*CLPO\s+D e s c r i p t i o n\s+Occupancy\s+Composition/
+          h["CLPO"] = []
+          while line = getline.call
+            break if line =~ /^\s*$/
+            num = Integer(line[0, 5])
+            label1 = line[5, 6].strip
+            desc = line[11, 30].strip
+            desc =~ /\s*([-A-Za-z0-9]+)(,\s*(.*$))?/
+            desc1 = $1
+            desc2 = ($3 || "")
+            if desc2 =~ /^(.*)*\(NB\)\s*$/ && label1 == ""
+              label1 = "(NB)"
+              desc2 = $1.strip
+            end
+            #  like ["(BD)", "C1-H3", "Io = 0.2237"]
+            h["CLPO"][num - 1] = [label1, desc1, desc2]
+          end
+        elsif line =~ /^ -NAO_Molden_File: (\S*)/
+          mfiles["NAO"] = $1
+        elsif line =~ /^ -LHO_Molden_File: (\S*)/
+          mfiles["LHO"] = $1
+        elsif line =~ /^ -CLPO_Molden_File: (\S*)/
+          mfiles["CLPO"] = $1
+        elsif line =~ /^ -PNAO_Molden_File: (\S*)/
+          mfiles["PNAO"] = $1
+        elsif line =~ /^ -AHO_Molden_File: (\S*)/
+          mfiles["AHO"] = $1
+        elsif line =~ /^ -LPO_Molden_File: (\S*)/
+          mfiles["LPO"] = $1
+        end
+      end
+      fp.close
+      #  Read molden files
+      mfiles.each { |key, value|
+        fp = Kernel.open(value, "rt") rescue fp = nil
+        if fp
+          print("Importing #{value}.\n")
+          res = sub_load_molden(fp, key)
+          if res
+            #  Some kind of orbital based on AO
+            h["AO/#{key}"] = LAMatrix.new(res[:mo])
+          end
+          fp.close
+        end
+      }
+      @nbo = h
+      return true
+    rescue => e
+      $stderr.write(e.message + "\n")
+      $stderr.write(e.backtrace.inspect + "\n")
+    end
+  end
+
   def loadout(filename)
   retval = false
   fp = open(filename, "rb")
@@ -756,14 +880,20 @@ class Molecule
         retval = sub_load_psi4_log(fp)
         if retval
           #  If .molden file exists, then try to read it
-          mname = filename.gsub(/\.\w*$/, ".molden")
+          namepath = filename.gsub(/\.\w*$/, "")
+          mname = "#{namepath}.molden"
           if File.exists?(mname)
             fp2 = open(mname, "rb")
             if fp2
               flag = sub_load_molden(fp2)
               fp2.close
+              status = (flag ? 0 : -1)
             end
           end
+          if File.exists?("#{namepath}.janpa.log")
+            flag = sub_load_janpa_log(namepath)
+            status = (flag ? 0 : -1)
+          end
         end
         break
       end
index 86a05e0..673a60e 100644 (file)
@@ -91,6 +91,8 @@
                E4059FDD28C46A6E0052B36B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4FC7A16183E51570064FB2E /* Foundation.framework */; };
                E4059FDE28C46A6E0052B36B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4FC7B58183E53710064FB2E /* WebKit.framework */; };
                E4059FDF28C46A6E0052B36B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4FC7CAC183F953E0064FB2E /* AudioToolbox.framework */; };
+               E40C8B3328E85322003CE26E /* JANPA in Resources */ = {isa = PBXBuildFile; fileRef = E40C8B3228E8522F003CE26E /* JANPA */; };
+               E40C8B3428E85357003CE26E /* JANPA in Resources */ = {isa = PBXBuildFile; fileRef = E40C8B3228E8522F003CE26E /* JANPA */; };
                E41251B828CD92A100E12983 /* MyListCtrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E41251B728CD92A100E12983 /* MyListCtrl.cpp */; };
                E41A7F962307D61200C65830 /* bitmaps in Resources */ = {isa = PBXBuildFile; fileRef = E41A7F952307D61200C65830 /* bitmaps */; };
                E420BDEF1885746700A2B983 /* ConsoleFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E420BDE81885746700A2B983 /* ConsoleFrame.cpp */; };
                E403568828D0C8C4008E2C46 /* textctrl_msw.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = textctrl_msw.cpp; sourceTree = "<group>"; };
                E4059FE428C46A6E0052B36B /* MolbyMacLegacy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MolbyMacLegacy.app; sourceTree = BUILT_PRODUCTS_DIR; };
                E4059FE628C46B320052B36B /* Molby_MacLegacy-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Molby_MacLegacy-Info.plist"; sourceTree = "<group>"; };
+               E40C8B3228E8522F003CE26E /* JANPA */ = {isa = PBXFileReference; lastKnownFileType = folder; name = JANPA; path = ../JANPA; sourceTree = "<group>"; };
                E41251B728CD92A100E12983 /* MyListCtrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MyListCtrl.cpp; sourceTree = "<group>"; };
                E41251BA28CD92AD00E12983 /* MyListCtrl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyListCtrl.h; sourceTree = "<group>"; };
                E4196C65190B2FA500183E62 /* menu.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = "<group>"; };
                                E4ACACE418C6D32300F08B67 /* ortep3 */,
                                E4FC7C16183E54730064FB2E /* Molby-Info.plist */,
                                E4059FE628C46B320052B36B /* Molby_MacLegacy-Info.plist */,
+                               E40C8B3228E8522F003CE26E /* JANPA */,
                        );
                        name = Resources;
                        sourceTree = "<group>";
                        isa = PBXResourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               E40C8B3328E85322003CE26E /* JANPA in Resources */,
                                E4FC7F821840C22B0064FB2E /* molby_icon.icns in Resources */,
                                E4FC77D7183E4FFE0064FB2E /* Scripts in Resources */,
                                E4FC7802183E503E0064FB2E /* amber11 in Resources */,
                        isa = PBXResourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               E40C8B3428E85357003CE26E /* JANPA in Resources */,
                                E4059F9B28C46A6E0052B36B /* molby_icon.icns in Resources */,
                                E4059F9C28C46A6E0052B36B /* Scripts in Resources */,
                                E4059F9D28C46A6E0052B36B /* amber11 in Resources */,
index c075710..8043438 100755 (executable)
@@ -1419,7 +1419,7 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)
        bool progress_panel = false;
        char buf[256];
        wxString cmdstr(cmdline, WX_DEFAULT_CONV);
-       wxLongLong startTime;
+    wxLongLong startTime, lastTime, presentTime;
 
        if (m_process != NULL)
                return -1;  //  Another process is already running (CallSubProcess() allows only one subprocess)
@@ -1433,7 +1433,7 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)
         ShowProgressPanel(buf);
                progress_panel = true;
        }
-       startTime = wxGetUTCTimeMillis();
+       startTime = lastTime = wxGetUTCTimeMillis();
        
        //  Create log file in the document home directory
 #if LOG_SUBPROCESS
@@ -1479,8 +1479,9 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)
     wxString bufstr;
     wxString buferrstr;
     while (1) {
-        int len1 = m_process->GetLine(bufstr);
-        if (len1 > 0) {
+        int len1, len2;
+        lastTime = wxGetUTCTimeMillis();
+        while ((len1 = m_process->GetLine(bufstr)) > 0) {
 #if LOG_SUBPROCESS
             dateTime.SetToCurrent();
             fprintf(fplog, "%s[STDOUT]%s", (const char *)(dateTime.FormatISOCombined(' ')), (const char *)bufstr);
@@ -1495,9 +1496,13 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)
                 MyAppCallback_setConsoleColor(0);
                 MyAppCallback_showScriptMessage("%s", (const char *)bufstr);
             }
+            presentTime = wxGetUTCTimeMillis();
+            if (presentTime > lastTime + 25) {
+                presentTime = lastTime;
+                break;
+            }
         }
-        int len2 = m_process->GetErrorLine(buferrstr);
-        if (len2 > 0) {
+        while ((len2 = m_process->GetErrorLine(buferrstr)) > 0) {
 #if LOG_SUBPROCESS
             dateTime.SetToCurrent();
             fprintf(fplog, "%s[STDERR]%s", (const char *)(dateTime.FormatISOCombined(' ')), buf);
@@ -1510,8 +1515,12 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)
                 MyAppCallback_showScriptMessage("\n%s", (const char *)buferrstr);
                 MyAppCallback_setConsoleColor(0);
             }
+            presentTime = wxGetUTCTimeMillis();
+            if (presentTime > lastTime + 25) {
+                presentTime = lastTime;
+                break;
+            }
         }
-
         if (len1 < 0 && len2 < 0) {
             //  The standard/error outputs are exhausted; the process should have terminated
             //  (Normally, this should be detected by wxBetterProcess::OnTerminate())
@@ -1523,12 +1532,11 @@ MyApp::CallSubProcess(const char *cmdline, const char *procname, int (*callback)
             if (callback_result != 0)
                 interrupted = true;
         }
+        ::wxSafeYield();  //  This allows updating console and wxProcess status
         if (progress_panel) {
             SetProgressValue(-1);
             if (IsInterrupted())
                 interrupted = true;
-        } else {
-            ::wxSafeYield();  //  This allows updating console and wxProcess status
         }
 
 #if LOG_SUBPROCESS