From 3e68dd7355874dde5a218c8ee089abb3dc2027fb Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 14:33:12 -0400 Subject: [PATCH 1/8] update Chemkin visualization code to no longer rely on deprecated code --- rmgweb/rmg/models.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/rmgweb/rmg/models.py b/rmgweb/rmg/models.py index 12375a7c..8165e389 100644 --- a/rmgweb/rmg/models.py +++ b/rmgweb/rmg/models.py @@ -75,16 +75,34 @@ def __init__(self, *args, **kwargs): dict_file = models.FileField(upload_to=uploadTo('RMG_Dictionary.txt'), verbose_name='RMG Dictionary', blank=True, null=True) foreign = models.BooleanField(verbose_name="Not an RMG-generated Chemkin file") + def save_html_file(path, read_comments=True): + """ + Save an output HTML file. + """ + from rmgpy.rmg.model import CoreEdgeReactionModel + from rmgpy.rmg.output import save_output_html + from rmgpy.chemkin import load_chemkin_file + + chemkin_path = os.path.join(path, 'chemkin', 'chem.inp') + dictionary_path = os.path.join(path, 'RMG_Dictionary.txt') + model = CoreEdgeReactionModel() + model.core.species, model.core.reactions = load_chemkin_file(chemkin_path, dictionary_path, + read_comments=read_comments) + output_path = os.path.join(path, 'output.html') + species_path = os.path.join(path, 'species') + if not os.path.isdir(species_path): + os.makedirs(species_path) + save_output_html(output_path, model) + def createOutput(self): """ Generate output html file from the path containing chemkin and dictionary files. """ - from rmgpy.chemkin import save_html_file if self.foreign: # Chemkin file was not from RMG, do not parse the comments when visualizing the file. - save_html_file(self.path, read_comments=False) + self.save_html_file(self.path, read_comments=False) else: - save_html_file(self.path) + self.save_html_file(self.path) def createDir(self): """ From 32793e9e51c148c126b9d187cc05aa0c9bdb648d Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 14:47:57 -0400 Subject: [PATCH 2/8] remove code related to RMG-Java --- rmgweb/database/tools.py | 239 ---------------------------------- rmgweb/database/urls.py | 3 - rmgweb/database/views.py | 54 -------- rmgweb/rmg/forms.py | 9 -- rmgweb/rmg/templates/rmg.html | 3 - rmgweb/rmg/urls.py | 6 - rmgweb/rmg/views.py | 54 -------- rmgweb/tests/testRMGTools.py | 82 ------------ 8 files changed, 450 deletions(-) diff --git a/rmgweb/database/tools.py b/rmgweb/database/tools.py index 48c8b202..98a42c3a 100644 --- a/rmgweb/database/tools.py +++ b/rmgweb/database/tools.py @@ -485,245 +485,6 @@ def reactionHasReactants(reaction, reactants): return same_species_lists(reaction.reactants, reactants, strict=False) -def getRMGJavaKineticsFromReaction(reaction): - """ - Get the kinetics for the given `reaction` (with reactants and products as :class:`Species`) - - Returns a copy of the reaction, with kinetics estimated by Java. - """ - reactant_list = [species.molecule[0] for species in reaction.reactants] - product_list = [species.molecule[0] for species in reaction.products] - reaction_list = getRMGJavaKinetics(reactant_list, product_list) - # assert len(reactionList) == 1 - if len(reaction_list) > 1: - print("WARNING - RMG-Java identified {0} reactions that match {1!s} instead of 1".format(len(reaction_list), reaction)) - reaction_list[0].kinetics.comment += "\nWARNING - RMG-Java identified {0} reactions that match this. These kinetics are just from one of them.".format(len(reaction_list)) - if len(reaction_list) == 0: - print("WARNING - RMG-Java could not find the reaction {0!s}".format(reaction)) - return None - return reaction_list[0] - - -def getRMGJavaKinetics(reactantList, productList=None): - """ - Get the kinetics for the given `reaction` as estimated by RMG-Java. The - reactants and products of the given reaction should be :class:`Molecule` - objects. - - This is done by querying a socket running RMG-Java as a service. We - construct the input file for a PopulateReactions job, pass that as input - to the RMG-Java service, then parse the output to find the kinetics of - the reaction we are interested in. - """ - - def formSpecies(species): - """ - This function takes a species string from RMG-Java containing both name - and adjlist and returns them separately. - """ - lines = species.split("\n") - species_name = lines[0] - adjlist = "\n".join(lines[1:]) - return species_name, adjlist - - def cleanResponse(response): - """ - This function cleans up response from PopulateReactions server and gives a - species dictionary and reactions list. - """ - - # Split species dictionary from reactions list - response = response.split("\n\n\n") - species_list = response[0].split("\n\n") - reactions = response[1].split("\n\n") - reactions = reactions[1] - - # split species into adjacency lists with names - species_dict = [formSpecies(item) for item in species_list] - - # split reactions into list of single line reactions - reactions_list = reactions.split("\n") - - return species_dict, reactions_list - - def searchReaction(reactionline, reactantNames, productNames): - """ - Reads reaction line and returns True if reaction occurs: - reactant1 + reactant2 --> product1 + product2 - - Finds both bimolecular and unimolecular reactions for only 1 reactant input, or only 1 product. - (reactants and products could be in either order 1,2 or 2,1) - """ - lines = reactionline.split("\t") - reaction_string = lines[0] - reactants, products = reaction_string.split(" --> ") - reactants = reactants.split(' + ') - products = products.split(' + ') - - reactants_match = len(reactantNames) == 0 - if len(reactantNames) == len(reactants): - reactants_match = sorted(reactants) == sorted(reactantNames) - elif len(reactantNames) == 1 and len(reactants) > 1: - reactants_match = all([r == reactantNames[0] for r in reactants]) - - products_match = len(productNames) == 0 - if len(productNames) == len(products): - products_match = sorted(products) == sorted(productNames) - elif len(productNames) == 1 and len(products) > 1: - products_match = all([p == productNames[0] for p in products]) - - return (reactants_match and products_match) - - def extractKinetics(reactionline): - """ - Takes a reaction line from RMG and creates Arrhenius object from - the kinetic data, as well as extracts names of reactants, products and comments. - Units from RMG-Java are in cm3, mol, s. - Reference Temperature T0 = 1 K. - """ - lines = reactionline.split("\t") - - reaction_string = lines[0] - reactants, products = reaction_string.split(" --> ") - reactants = reactants.split(" + ") - products = products.split(" + ") - - if len(reactants) == 1: - Aunits = "s^-1" - elif len(reactants) == 2: - Aunits = "cm**3/mol/s" - else: # 3 reactants? - Aunits = "cm**6/(mol^2*s)" - - kinetics = Arrhenius( - A=(float(lines[1]), Aunits), - n=float(lines[2]), - Ea=(float(lines[3]), "kcal/mol"), - T0=(1, "K"), - ) - - comments = "\t".join(lines[4:]) - kinetics.comment = "Estimated by RMG-Java:\n" + comments - entry = Entry(long_desc=comments) - - return reactants, products, kinetics, entry - - def identifySpecies(species_dict, molecule): - """ - Given a species_dict list and the species adjacency list, identifies - whether species is found in the list and returns its name if found. - """ - resonance_isomers = molecule.generate_resonance_structures() - for name, adjlist in species_dict: - list_molecule = Molecule().from_adjacency_list(adjlist, saturate_h=True) - for isomer in resonance_isomers: - if isomer.is_isomorphic(list_molecule): - return name - return False - - product_list = productList or [] - reaction_list = [] - - # Generate species list for Java request - pop_reactants = '' - added_reactants = set() - for index, reactant in enumerate(reactantList): - assert isinstance(reactant, Molecule) - reactant.clear_labeled_atoms() - for r in added_reactants: - if r.is_isomorphic(reactant): - break # already added this reactant - else: # exhausted the added_reactants list without finding duplicate and breaking - added_reactants.add(reactant) - pop_reactants += 'reactant{0:d} (molecule/cm3) 1\n{1}\n\n'.format(index+1, reactant.to_adjacency_list(remove_lone_pairs=True)) - pop_reactants += 'END\n' - - # First send search request to PopulateReactions server - client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client_socket.settimeout(10) - try: - client_socket.connect(("localhost", 5000)) - except IOError: - print('Unable to query RMG-Java for kinetics. (Is the RMG-Java server running?)', file=sys.stderr) - sys.stderr.flush() - return reaction_list - - # Send request to server - print("SENDING REQUEST FOR RMG-JAVA SEARCH TO SERVER") - client_socket.sendall(pop_reactants) - partial_response = client_socket.recv(512) - response = partial_response - while partial_response: - partial_response = client_socket.recv(512) - response += partial_response - client_socket.close() - print("FINISHED REQUEST. CLOSED CONNECTION TO SERVER") - # Clean response from server - try: - species_dict, reactions_list = cleanResponse(response) - except: - # Return an empty reaction list if an error occurred on the java server side, - # instead of having the website crash. - print("AN ERROR OCCURRED IN THE JAVA SERVER.") - print(response) - return [] - - # Name the species in reaction - reactant_names = [] - for reactant in reactantList: - reactant_names.append(identifySpecies(species_dict, reactant)) - product_names = [] - for product in product_list: - product_names.append(identifySpecies(species_dict, product)) - # identifySpecies(species_dict, product) returns "False" if it can't find product - if not identifySpecies(species_dict, product): - print("Could not find this requested product in the species dictionary from RMG-Java:") - print(product) - - species_dict = dict([(key, Molecule().from_adjacency_list(value, saturate_h=True)) for key, value in species_dict]) - - # Both products were actually found in species dictionary or were blank - reaction = None - if all(product_names): - - # Constants for all entries - degeneracy = 1 - - # Search for da Reactions - print('Searching output for desired reaction...\n') - for reaction_line in reactions_list: - if reaction_line.strip().startswith('DUP'): - print("WARNING - DUPLICATE REACTION KINETICS ARE NOT BEING SUMMED") - # if set, the `reaction` variable should still point to the reaction from the previous reactionline iteration - if reaction: - reaction.kinetics.comment += "\nWARNING - DUPLICATE REACTION KINETICS IDENTIFIED BUT NOT SUMMED" - continue # to next reaction line. - - reaction = None - # Search for both forward and backward reactions - indicator1 = searchReaction(reaction_line, reactant_names, product_names) - indicator2 = searchReaction(reaction_line, product_names, reactant_names) - if indicator1 or indicator2: - print('Found a matching reaction:') - print(reaction_line) - reactants, products, kinetics, entry = extractKinetics(reaction_line) - reaction = DepositoryReaction( - reactants=[species_dict[reactant] for reactant in reactants], - products=[species_dict[product] for product in products], - kinetics=kinetics, - degeneracy=degeneracy, - entry=entry, - ) - - reaction_list.append(reaction) - - # Return the reactions as containing Species objects, not Molecule objects - for reaction in reaction_list: - reaction.reactants = [Species(molecule=[reactant]) for reactant in reaction.reactants] - reaction.products = [Species(molecule=[product]) for product in reaction.products] - - return reaction_list - def getAbrahamAB(smiles): diff --git a/rmgweb/database/urls.py b/rmgweb/database/urls.py index 698b2635..5d941132 100644 --- a/rmgweb/database/urls.py +++ b/rmgweb/database/urls.py @@ -44,9 +44,6 @@ # Load the whole database into memory re_path(r'^load/?$', views.load, name='load'), - # Export to an RMG-Java database - re_path(r'^export_(?Pzip|tar\.gz)/?$', views.export, name='export'), - # Thermodynamics database re_path(r'^thermo/$', views.thermo, name='thermo'), re_path(r'^thermo/search/$', views.moleculeSearch, name='thermo-search'), diff --git a/rmgweb/database/views.py b/rmgweb/database/views.py index 088ded20..e84dfa36 100644 --- a/rmgweb/database/views.py +++ b/rmgweb/database/views.py @@ -121,50 +121,6 @@ def index(request): return render(request, 'database.html') -def export(request, type): - """ - Export the RMG database to the old RMG-Java format. - """ - # Build archive filenames from git hash and compression type - sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'], - cwd=rmgweb.settings.DATABASE_PATH)[:7] - base = 'RMG_database_{0}'.format(sha) - file_zip = '{0}.zip'.format(base) - file_tar = '{0}.tar.gz'.format(base) - if type == 'zip': - file = file_zip - elif type == 'tar.gz': - file = file_tar - - # Set output path - path = os.path.join(rmgweb.settings.PROJECT_PATH, '..', 'database', 'export') - output = os.path.join(path, 'RMG_database') - - # Assert archives do not already exist - if not os.path.exists(os.path.join(path, file)): - - # Export old database - cmd_export = ['python', rmgweb.settings.DATABASE_PATH + '../scripts/exportOldDatabase.py', output] - subprocess.check_call(cmd_export) - - # Compress database to zip - cmd_zip = ['zip', '-r', base, 'RMG_database'] - subprocess.check_output(cmd_zip, cwd=path) - - # Compress database to tar.gz - cmd_tar = ['tar', '-czf', file_tar, 'RMG_database'] - subprocess.check_output(cmd_tar, cwd=path) - - # Make compressed databases group-writable - os.chmod(os.path.join(path, file_zip), 0o664) - os.chmod(os.path.join(path, file_tar), 0o664) - - # Remove exported database - shutil.rmtree(output) - - # Redirect to requested compressed database - return HttpResponseRedirect('export/{0}'.format(file)) - ################################################################################################################################################# @@ -3045,16 +3001,6 @@ def kineticsGroupEstimateEntry(request, family, estimator, reactant1, product1, }) -def kineticsJavaEntry(request, entry, reactants_fig, products_fig, kineticsParameters, kineticsModel): - section = '' - subsection = '' - database_name = 'RMG-Java Database' - reference = '' - reference_type = '' - arrow = '⇔' - return render(request, 'kineticsEntry.html', {'section': section, 'subsection': subsection, 'databaseName': database_name, 'entry': entry, 'reactants': reactants_fig, 'arrow': arrow, 'products': products_fig, 'reference': reference, 'referenceType': reference_type, 'kinetics': entry.data}) - - def kineticsSearch(request): """ A view of a form for specifying a set of reactants to search the database diff --git a/rmgweb/rmg/forms.py b/rmgweb/rmg/forms.py index 48dba6ff..4d4744d2 100644 --- a/rmgweb/rmg/forms.py +++ b/rmgweb/rmg/forms.py @@ -63,15 +63,6 @@ class Meta(object): fields = '__all__' -class UploadDictionaryForm(forms.ModelForm): - """ - A Django form for uploading a RMG dictionary file. - """ - class Meta(object): - model = AdjlistConversion - fields = '__all__' - - class FluxDiagramForm(forms.ModelForm): """ A Django form for creating a flux diagram by uploading the files required. diff --git a/rmgweb/rmg/templates/rmg.html b/rmgweb/rmg/templates/rmg.html index 6defc4fb..4f9b19d0 100644 --- a/rmgweb/rmg/templates/rmg.html +++ b/rmgweb/rmg/templates/rmg.html @@ -23,15 +23,12 @@ and RMG-generated species dictionary.

  • Model Comparison: compare two RMG-generated models by supplying their individual chemkin files and species dictionaries.

    -

  • Convert Adjacency Lists: convert adjacency lists in a text file to old-style adjacency lists - which are compatible with RMG-Java.

  • Merge Models: merge two RMG-generated models by supplying their individual chemkin files and species dictionaries.

  • Generate Flux Diagram: generate a flux diagram video by supplying a RMG input file and completed mechanism, or with a customized set of concentration profiles from a Chemkin job.

  • Populate Reactions: generate all possible reactions from a set of initial species.

  • Plot Kinetics: plot forward and reverse kinetics by supplying a list of reactions in chemkin format along with a RMG dictionary.

    -

  • Create RMG-Java Kinetics Library: create an RMG-Java kinetics library for use as a seed-mechanism or reaction library by supplying a chemkin file along with a RMG dictionary.

  • Evaluate NASA Polynomial: View enthalpy, entropy, and heat capacity information in human readable format for a CHEMKIN format NASA polynomial.

  • Draw Functional Group: Draw an RMG group definition from its Adjacency List.

    diff --git a/rmgweb/rmg/urls.py b/rmgweb/rmg/urls.py index 668f9cec..1371a1e5 100644 --- a/rmgweb/rmg/urls.py +++ b/rmgweb/rmg/urls.py @@ -48,9 +48,6 @@ # Compare 2 RMG Models re_path(r'^compare', views.compareModels, name='compare-models'), - # Compare 2 RMG Models - re_path(r'^adjlist_conversion', views.convertAdjlists, name='convert-adjlists'), - # Merge 2 RMG Models re_path(r'^merge_models', views.mergeModels, name='merge-models'), @@ -63,9 +60,6 @@ # Plot Kinetics re_path(r'^plot_kinetics', views.plotKinetics, name='plot-kinetics'), - # Generate RMG-Java Kinetics Library - re_path(r'^java_kinetics_library', views.javaKineticsLibrary, name='java-kinetics-library'), - # Evaluate NASA Polynomial re_path(r'^evaluate_nasa', views.evaluateNASA, name='evaluate-nasa'), ] diff --git a/rmgweb/rmg/views.py b/rmgweb/rmg/views.py index 029b87b2..31b0339e 100644 --- a/rmgweb/rmg/views.py +++ b/rmgweb/rmg/views.py @@ -72,31 +72,6 @@ def convertChemkin(request): return render(request, 'chemkinUpload.html', {'form': form, 'path': path}) -def convertAdjlists(request): - """ - Allows user to upload a dictionary txt file and convert it back into old style adjacency lists in the form of a txt file. - """ - conversion = AdjlistConversion() - path = '' - conversion.deleteDir() - - if request.method == 'POST': - conversion.createDir() - form = UploadDictionaryForm(request.POST, request.FILES, instance=conversion) - if form.is_valid(): - form.save() - path = 'media/rmg/tools/adjlist_conversion/RMG_Dictionary.txt' - # Generate the output HTML file - conversion.createOutput() - # Go back to the network's main page - return render(request, 'dictionaryUpload.html', {'form': form, 'path': path}) - - # Otherwise create the form - else: - form = UploadDictionaryForm(instance=conversion) - - return render(request, 'dictionaryUpload.html', {'form': form, 'path': path}) - def compareModels(request): """ @@ -362,35 +337,6 @@ def plotKinetics(request): return render(request, 'plotKinetics.html', {'form': form}) -def javaKineticsLibrary(request): - """ - Allows user to upload chemkin files to generate a plot of reaction kinetics. - """ - - eval = False - - if request.method == 'POST': - chemkin = Chemkin() - chemkin.createDir() - form = UploadChemkinForm(request.POST, request.FILES, instance=chemkin) - if form.is_valid(): - form.save() - chemkin.createJavaKineticsLibrary() - eval = True - - return render(request, 'javaKineticsLibrary.html', - {'form': form, 'eval': eval}) - - # Otherwise create the form - else: - - chemkin = Chemkin() - chemkin.deleteDir() - form = UploadChemkinForm(instance=chemkin) - - return render(request, 'javaKineticsLibrary.html', {'form': form}) - - def evaluateNASA(request): """ Creates webpage form form entering a chemkin format NASA Polynomial and quickly diff --git a/rmgweb/tests/testRMGTools.py b/rmgweb/tests/testRMGTools.py index e63fde17..cf0e8740 100644 --- a/rmgweb/tests/testRMGTools.py +++ b/rmgweb/tests/testRMGTools.py @@ -341,85 +341,3 @@ def test_1(self): self.assertEqual(response.status_code, 200) - -class AdjlistConversionTest(TestCase): - - def test_0(self): - """ - Test that webpage gives expected response - """ - - response = self.client.get('/tools/adjlist_conversion/') - - self.assertEqual(response.status_code, 200) - - def test_1(self): - """ - Test basic functionality of /tools/adjlist_conversion/ - """ - - dict_file = os.path.join(os.path.dirname(rmgweb.__file__), 'tests', 'files', 'kinetics', 'species_dictionary.txt') - - with open(dict_file) as df: - response = self.client.post('/tools/adjlist_conversion/', {'dict_file': df}) - - self.assertEqual(response.status_code, 200) - - folder = os.path.join(settings.MEDIA_ROOT, 'rmg', 'tools', 'adjlist_conversion') - - # Check if inputs were correctly uploaded - dict_input = os.path.join(folder, 'species_dictionary.txt') - - self.assertTrue(os.path.isfile(dict_input), 'Dictionary file was not uploaded') - - # Check if outputs were correctly generated - html_output = os.path.join(folder, 'RMG_Dictionary.txt') - - self.assertTrue(os.path.isfile(html_output), 'Species dictionary was not generated') - - shutil.rmtree(folder) - - -class JavaLibraryTest(TestCase): - - def test_0(self): - """ - Test that webpage gives expected response - """ - - response = self.client.get('/tools/java_kinetics_library/') - - self.assertEqual(response.status_code, 200) - - def test_1(self): - """ - Test basic functionality of /tools/java_kinetics_library/ - """ - - chem_file = os.path.join(os.path.dirname(rmgweb.__file__), 'tests', 'files', 'kinetics', 'chem.inp') - dict_file = os.path.join(os.path.dirname(rmgweb.__file__), 'tests', 'files', 'kinetics', 'species_dictionary.txt') - - with open(chem_file) as cf, open(dict_file) as df: - response = self.client.post('/tools/java_kinetics_library/', {'chem_file': cf, 'dict_file': df}) - - self.assertEqual(response.status_code, 200) - - folder = os.path.join(settings.MEDIA_ROOT, 'rmg', 'tools', 'chemkin') - - # Check if inputs were correctly uploaded - chem_input = os.path.join(folder, 'chemkin', 'chem.inp') - dict_input = os.path.join(folder, 'RMG_Dictionary.txt') - - self.assertTrue(os.path.isfile(chem_input), 'Chemkin file was not uploaded') - self.assertTrue(os.path.isfile(dict_input), 'Dictionary file was not uploaded') - - # Check if outputs were correctly generated - reactions = os.path.join(folder, 'reactions.txt') - pdep = os.path.join(folder, 'pdepreactions.txt') - species = os.path.join(folder, 'species.txt') - - self.assertTrue(os.path.isfile(reactions), 'Reactions file was not generated') - self.assertTrue(os.path.isfile(pdep), 'Pdep reactions file was not generated') - self.assertTrue(os.path.isfile(species), 'Species file was not generated') - - shutil.rmtree(folder) From 4d7e4d9dc1983f8005ac05df10cde5111a126eef Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 14:54:35 -0400 Subject: [PATCH 3/8] remove additional references to rmg java --- rmgweb/rmg/models.py | 80 --------------------------- rmgweb/rmg/templates/fluxDiagram.html | 1 - 2 files changed, 81 deletions(-) diff --git a/rmgweb/rmg/models.py b/rmgweb/rmg/models.py index 8165e389..67d899f0 100644 --- a/rmgweb/rmg/models.py +++ b/rmgweb/rmg/models.py @@ -180,18 +180,6 @@ def getKinetics(self): return kinetics_data_list - def createJavaKineticsLibrary(self): - """ - Generates java reaction library files from your chemkin file. - """ - from rmgpy.chemkin import load_chemkin_file, save_java_kinetics_library - - chem_path = os.path.join(self.path, 'chemkin', 'chem.inp') - dict_path = os.path.join(self.path, 'RMG_Dictionary.txt') - spc_list, rxn_list = load_chemkin_file(chem_path, dict_path) - save_java_kinetics_library(self.path, spc_list, rxn_list) - return - class Diff(models.Model): """ @@ -284,71 +272,6 @@ def deleteDir(self): pass -class AdjlistConversion(models.Model): - """ - A Django model for converting new style adjlists to old style ones. - """ - - def __init__(self, *args, **kwargs): - super(AdjlistConversion, self).__init__(*args, **kwargs) - self.folder = os.path.join('rmg', 'tools', 'adjlist_conversion') - self.path = os.path.join(settings.MEDIA_ROOT, self.folder) - self.dictionary = os.path.join(self.path, 'species_dictionary.txt') - - dict_file = models.FileField(upload_to=uploadTo('species_dictionary.txt'), verbose_name='RMG Dictionary') - - def createOutput(self): - """ - Generate output html file from the path containing chemkin and dictionary files. - """ - - spc_list = [] - with open(self.dictionary, 'r') as f: - adjlist = '' - for line in f: - if line.strip() == '' and adjlist.strip() != '': - # Finish this adjacency list - species = Species().from_adjacency_list(adjlist) - spc_list.append(species) - adjlist = '' - else: - if "InChI" in line: - line = line.split()[0] + '\n' - if '//' in line: - index = line.index('//') - line = line[0:index] - adjlist += line - - with open(os.path.join(self.path, 'RMG_Dictionary.txt'), 'w') as f: - for spec in spc_list: - try: - f.write(spec.molecule[0].to_adjacency_list(label=spec.label, remove_h=True, old_style=True)) - f.write('\n') - except: - raise Exception('Ran into error saving adjlist for species {0}. It may not be compatible with old adjacency list format.'.format(spec)) - - def createDir(self): - """ - Create the directory (and any other needed parent directories) that - the Network uses for storing files. - """ - try: - os.makedirs(self.path) - except OSError: - # Fail silently on any OS errors - pass - - def deleteDir(self): - """ - Clean up everything by deleting the directory - """ - import shutil - try: - shutil.rmtree(self.path) - except OSError: - pass - - class FluxDiagram(models.Model): """ A Django model for generating a flux diagram using RMG-Py. @@ -363,7 +286,6 @@ def __init__(self, *args, **kwargs): chem_file = models.FileField(upload_to=uploadTo('chem.inp'), verbose_name='Chemkin File') dict_file = models.FileField(upload_to=uploadTo('species_dictionary.txt'), verbose_name='RMG Dictionary') chem_output = models.FileField(upload_to=uploadTo('chemkin_output.out'), verbose_name='Chemkin Output File (Optional)', blank=True, null=True) - java = models.BooleanField(default=False, verbose_name='From RMG-Java') max_nodes = models.PositiveIntegerField(default=50, verbose_name='Maximum Nodes') max_edges = models.PositiveIntegerField(default=50, verbose_name='Maximum Edges') time_step = models.FloatField(default=1.25, verbose_name='Multiplicative Time Step Factor') @@ -396,8 +318,6 @@ def createOutput(self, arguments): ] if arguments['chem_output']: command.insert(5, arguments['chem_output']) - if arguments['java']: - command.append('--java') # It is important to learn why flux diagram generation fails try: diff --git a/rmgweb/rmg/templates/fluxDiagram.html b/rmgweb/rmg/templates/fluxDiagram.html index 91f19538..fdc00ac3 100644 --- a/rmgweb/rmg/templates/fluxDiagram.html +++ b/rmgweb/rmg/templates/fluxDiagram.html @@ -76,7 +76,6 @@

    Chemkin File: {{form.chem_file}}

    RMG Dictionary: {{form.dict_file}}

    Chemkin Output File (Optional): {{form.chem_output}} -

    From RMG-Java: {{form.java}}

    Advanced Options

    From f685ce9e1353325c3d48baf7d505593b0b4cecce Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 15:03:49 -0400 Subject: [PATCH 4/8] remove javakineticslibrary page --- rmgweb/rmg/templates/javaKineticsLibrary.html | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 rmgweb/rmg/templates/javaKineticsLibrary.html diff --git a/rmgweb/rmg/templates/javaKineticsLibrary.html b/rmgweb/rmg/templates/javaKineticsLibrary.html deleted file mode 100644 index 90f2f197..00000000 --- a/rmgweb/rmg/templates/javaKineticsLibrary.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "base.html" %} - - - -{% block title %}Convert Chemkin File{% endblock %} - -{% block navbar_items %} -
  • RMG Tools
  • -
  • Create RMG-Java Kinetics Library
  • -{% endblock %} - -{% block sidebar_items %} -{% endblock %} - -{% block page_title %}Create RMG-Java Kinetics Library{% endblock %} - -{% block page_body %} - -

    -Upload your chemkin file and its associated RMG Dictionary text file to generate files for -creating a RMG-Java kinetics library: reactions.txt, pdepreactions.txt, and species.txt -

    These files should be dropped into a single folder within the kinetics_libraries folder. -

    -
    - - -
    {% csrf_token %} -{{ form.as_p }} -

    -
    - - -{% if eval %} -
    -Your library files are below: -
    To save, right click and select "Save as..." -

    -reactions.txt -
    -pdepreactions.txt -
    -species.txt -{% endif %} - -{% endblock %} From 0854b21e63a1d3b6895808dd401076db1d431778 Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 15:51:35 -0400 Subject: [PATCH 5/8] attempt to resolve django error by adding default value --- rmgweb/rmg/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rmgweb/rmg/models.py b/rmgweb/rmg/models.py index 67d899f0..d8615570 100644 --- a/rmgweb/rmg/models.py +++ b/rmgweb/rmg/models.py @@ -71,7 +71,7 @@ def __init__(self, *args, **kwargs): self.folder = os.path.join('rmg', 'tools', 'chemkin') self.path = os.path.join(settings.MEDIA_ROOT, self.folder) - chem_file = models.FileField(upload_to=uploadTo(os.path.join('chemkin', 'chem.inp')), verbose_name='Chemkin File') + chem_file = models.FileField(upload_to=uploadTo(os.path.join('chemkin', 'chem.inp')), verbose_name='Chemkin File', blank=True, null=True) dict_file = models.FileField(upload_to=uploadTo('RMG_Dictionary.txt'), verbose_name='RMG Dictionary', blank=True, null=True) foreign = models.BooleanField(verbose_name="Not an RMG-generated Chemkin file") From 83d9a22d513d8707681c0454039557120f6c4db0 Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 16:03:19 -0400 Subject: [PATCH 6/8] further include null and blank defaultvalues to avoid errors in migration --- rmgweb/rmg/models.py | 8 ++++---- rmgweb/tests/testRMGTools.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/rmgweb/rmg/models.py b/rmgweb/rmg/models.py index d8615570..2cd3a268 100644 --- a/rmgweb/rmg/models.py +++ b/rmgweb/rmg/models.py @@ -195,11 +195,11 @@ def __init__(self, *args, **kwargs): self.chemkin2 = os.path.join(self.path, 'chem2.inp') self.dict2 = os.path.join(self.path, 'RMG_Dictionary2.txt') - chem_file1 = models.FileField(upload_to=uploadTo('chem1.inp'), verbose_name='Model 1: Chemkin File') - dict_file1 = models.FileField(upload_to=uploadTo('RMG_Dictionary1.txt'), verbose_name='Model 1: RMG Dictionary') + chem_file1 = models.FileField(upload_to=uploadTo('chem1.inp'), verbose_name='Model 1: Chemkin File', blank=True, null=True) + dict_file1 = models.FileField(upload_to=uploadTo('RMG_Dictionary1.txt'), verbose_name='Model 1: RMG Dictionary', blank=True, null=True) foreign1 = models.BooleanField(verbose_name="Model 1 not an RMG-generated Chemkin file") - chem_file2 = models.FileField(upload_to=uploadTo('chem2.inp'), verbose_name='Model 2: Chemkin File') - dict_file2 = models.FileField(upload_to=uploadTo('RMG_Dictionary2.txt'), verbose_name='Model 2: RMG Dictionary') + chem_file2 = models.FileField(upload_to=uploadTo('chem2.inp'), verbose_name='Model 2: Chemkin File', blank=True, null=True) + dict_file2 = models.FileField(upload_to=uploadTo('RMG_Dictionary2.txt'), verbose_name='Model 2: RMG Dictionary', blank=True, null=True) foreign2 = models.BooleanField(verbose_name="Model 2 not an RMG-generated Chemkin file") def createOutput(self): diff --git a/rmgweb/tests/testRMGTools.py b/rmgweb/tests/testRMGTools.py index cf0e8740..b5748c75 100644 --- a/rmgweb/tests/testRMGTools.py +++ b/rmgweb/tests/testRMGTools.py @@ -54,8 +54,8 @@ def test_1(self): Test post functionality of /tools/chemkin/ """ - chem_file = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem1.inp') - dict_file = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary1.txt') + chem_file = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem1.inp', blank=True, null=True) + dict_file = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary1.txt', blank=True, null=True) with open(chem_file) as cf, open(dict_file) as df: response = self.client.post('/tools/chemkin/', {'chem_file': cf, 'dict_file': df}) @@ -96,10 +96,10 @@ def test_1(self): Test basic functionality of /tools/compare/ """ - chem_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem1.inp') - dict_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary1.txt') - chem_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem2.inp') - dict_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary2.txt') + chem_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem1.inp', blank=True, null=True) + dict_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary1.txt', blank=True, null=True) + chem_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem2.inp', blank=True, null=True) + dict_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary2.txt', blank=True, null=True) with open(chem_file1) as cf1, open(dict_file1) as df1, open(chem_file2) as cf2, open(dict_file2) as df2: response = self.client.post('/tools/compare/', {'chem_file1': cf1, 'dict_file1': df1, 'chem_file2': cf2, 'dict_file2': df2}) @@ -140,10 +140,10 @@ def test_3(self): Test basic functionality of /tools/merge_models/ """ - chem_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem1.inp') - dict_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary1.txt') - chem_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem2.inp') - dict_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary2.txt') + chem_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem1.inp', blank=True, null=True) + dict_file1 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary1.txt', blank=True, null=True) + chem_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'chem2.inp', blank=True, null=True) + dict_file2 = os.path.join(rmgpy.get_path(), 'tools', 'data', 'diffmodels', 'species_dictionary2.txt', blank=True, null=True) with open(chem_file1) as cf1, open(dict_file1) as df1, open(chem_file2) as cf2, open(dict_file2) as df2: response = self.client.post('/tools/merge_models/', {'chem_file1': cf1, 'dict_file1': df1, 'chem_file2': cf2, 'dict_file2': df2}) From e3676dc23725c8c5e63d44bab10c9f02a0ffd01c Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 16:27:36 -0400 Subject: [PATCH 7/8] refactor saveHtmlFile and make camelcase --- rmgweb/main/tools.py | 23 +++++++++++++++++++++++ rmgweb/rmg/models.py | 25 +++---------------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/rmgweb/main/tools.py b/rmgweb/main/tools.py index 99840180..082d0367 100644 --- a/rmgweb/main/tools.py +++ b/rmgweb/main/tools.py @@ -31,6 +31,12 @@ import urllib import math +import os + + +from rmgpy.rmg.model import CoreEdgeReactionModel +from rmgpy.rmg.output import save_output_html +from rmgpy.chemkin import load_chemkin_file from django.urls import reverse from rmgpy.molecule.group import Group from rmgpy.molecule.molecule import Molecule @@ -182,3 +188,20 @@ def getStructureMarkup(item): else: structure = '' return structure + +################################################################################ + +def saveHtmlFile(path, read_comments=True): + """ + Save an output HTML file. + """ + chemkin_path = os.path.join(path, 'chemkin', 'chem.inp') + dictionary_path = os.path.join(path, 'RMG_Dictionary.txt') + model = CoreEdgeReactionModel() + model.core.species, model.core.reactions = load_chemkin_file(chemkin_path, dictionary_path, + read_comments=read_comments) + output_path = os.path.join(path, 'output.html') + species_path = os.path.join(path, 'species') + if not os.path.isdir(species_path): + os.makedirs(species_path) + save_output_html(output_path, model) \ No newline at end of file diff --git a/rmgweb/rmg/models.py b/rmgweb/rmg/models.py index 2cd3a268..1dadaa9d 100644 --- a/rmgweb/rmg/models.py +++ b/rmgweb/rmg/models.py @@ -75,34 +75,15 @@ def __init__(self, *args, **kwargs): dict_file = models.FileField(upload_to=uploadTo('RMG_Dictionary.txt'), verbose_name='RMG Dictionary', blank=True, null=True) foreign = models.BooleanField(verbose_name="Not an RMG-generated Chemkin file") - def save_html_file(path, read_comments=True): - """ - Save an output HTML file. - """ - from rmgpy.rmg.model import CoreEdgeReactionModel - from rmgpy.rmg.output import save_output_html - from rmgpy.chemkin import load_chemkin_file - - chemkin_path = os.path.join(path, 'chemkin', 'chem.inp') - dictionary_path = os.path.join(path, 'RMG_Dictionary.txt') - model = CoreEdgeReactionModel() - model.core.species, model.core.reactions = load_chemkin_file(chemkin_path, dictionary_path, - read_comments=read_comments) - output_path = os.path.join(path, 'output.html') - species_path = os.path.join(path, 'species') - if not os.path.isdir(species_path): - os.makedirs(species_path) - save_output_html(output_path, model) - def createOutput(self): """ Generate output html file from the path containing chemkin and dictionary files. """ if self.foreign: # Chemkin file was not from RMG, do not parse the comments when visualizing the file. - self.save_html_file(self.path, read_comments=False) + saveHtmlFile(self.path, read_comments=False) else: - self.save_html_file(self.path) + saveHtmlFile(self.path) def createDir(self): """ @@ -199,7 +180,7 @@ def __init__(self, *args, **kwargs): dict_file1 = models.FileField(upload_to=uploadTo('RMG_Dictionary1.txt'), verbose_name='Model 1: RMG Dictionary', blank=True, null=True) foreign1 = models.BooleanField(verbose_name="Model 1 not an RMG-generated Chemkin file") chem_file2 = models.FileField(upload_to=uploadTo('chem2.inp'), verbose_name='Model 2: Chemkin File', blank=True, null=True) - dict_file2 = models.FileField(upload_to=uploadTo('RMG_Dictionary2.txt'), verbose_name='Model 2: RMG Dictionary', blank=True, null=True) + dict_file2 = models.FileField(upload_to=uploadTo('RMG_Dictionary2.txt'), verbose_name='Model 2: RMG Dictionary', ) foreign2 = models.BooleanField(verbose_name="Model 2 not an RMG-generated Chemkin file") def createOutput(self): From 21aa9a978997534ec45031a221057008594842f2 Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Tue, 9 Jul 2024 16:30:14 -0400 Subject: [PATCH 8/8] fix nullable for diff --- rmgweb/rmg/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rmgweb/rmg/models.py b/rmgweb/rmg/models.py index 1dadaa9d..62809a05 100644 --- a/rmgweb/rmg/models.py +++ b/rmgweb/rmg/models.py @@ -180,7 +180,7 @@ def __init__(self, *args, **kwargs): dict_file1 = models.FileField(upload_to=uploadTo('RMG_Dictionary1.txt'), verbose_name='Model 1: RMG Dictionary', blank=True, null=True) foreign1 = models.BooleanField(verbose_name="Model 1 not an RMG-generated Chemkin file") chem_file2 = models.FileField(upload_to=uploadTo('chem2.inp'), verbose_name='Model 2: Chemkin File', blank=True, null=True) - dict_file2 = models.FileField(upload_to=uploadTo('RMG_Dictionary2.txt'), verbose_name='Model 2: RMG Dictionary', ) + dict_file2 = models.FileField(upload_to=uploadTo('RMG_Dictionary2.txt'), verbose_name='Model 2: RMG Dictionary', blank=True, null=True) foreign2 = models.BooleanField(verbose_name="Model 2 not an RMG-generated Chemkin file") def createOutput(self):