Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 53 additions & 66 deletions src/neqsim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,70 +31,10 @@ def setDatabase(connectionString):
jneqsim.util.database.NeqSimDataBase.setCreateTemporaryTables(True)


def open_neqsim(filename, allow_all=True, wildcard_permission=None):
"""
Open and deserialize a NEQSim Java object from either a .gz or .zip XStream-serialized XML file.

Supports:
- GZipped XML: .xml.gz
- Zipped XML: .zip (must contain a file named 'process.xml')

Returns:
object: The deserialized Java object.
"""
import os

# Instantiate XStream
XStream = jpype.JClass("com.thoughtworks.xstream.XStream")
xstream = XStream()

# Configure security
security_pkg = jpype.JPackage("com.thoughtworks.xstream.security")
if allow_all:
xstream.addPermission(security_pkg.AnyTypePermission.ANY)
elif wildcard_permission is not None:
xstream.addPermission(security_pkg.WildcardTypePermission(wildcard_permission))

# Detect file format by extension
ext = os.path.splitext(filename)[-1].lower()

try:
if ext == ".gz":
with gzip.open(filename, "rb") as f:
xml_bytes = f.read()
elif ext == ".zip":
import zipfile

with zipfile.ZipFile(filename, "r") as zf:
with zf.open("process.xml") as f:
xml_bytes = f.read()
else:
raise ValueError(f"Unsupported file extension: {ext}")
except Exception as e:
print(f"[open_neqsim] Failed to read/compress/decompress file: {e}")
return None

# Deserialize
try:
xml_str = xml_bytes.decode("utf-8")
java_object = xstream.fromXML(xml_str)
return java_object
except Exception as e:
print(f"[open_neqsim] Failed to deserialize object: {e}")
return None


def save_neqsim(javaobject, filename):
"""
Serialize a Java object (e.g., NEQSim ProcessSystem) to XML using XStream,
and save it as a compressed ZIP file containing one XML file.

Args:
javaobject: A Java object that XStream can serialize.
filename (str): The path to the ZIP file to write (e.g., "myProcess.zip").

Returns:
bool: True if the file is successfully written, False otherwise.
Save a NEQSim Java object as a compressed ZIP file with any filename and extension.
Inside, the XML will be stored as 'process.xml'.
"""
if not jpype.isJVMStarted():
raise RuntimeError(
Expand All @@ -111,32 +51,79 @@ def save_neqsim(javaobject, filename):
OutputStreamWriter = jpype.JClass("java.io.OutputStreamWriter")
File = jpype.JClass("java.io.File")

# Create XStream instance and configure security
# Setup XStream
xstream = XStream()
xstream.allowTypesByWildcard(["neqsim.**"])

# Setup output stream and ZIP structure
# Prepare file output
file = File(filename)
fout = BufferedOutputStream(FileOutputStream(file))
zout = ZipOutputStream(fout)

# Use a fixed name for the XML inside the ZIP
# Fixed entry name inside ZIP
entry = ZipEntry("process.xml")
zout.putNextEntry(entry)

# Stream XML directly into ZIP
writer = OutputStreamWriter(zout, "UTF-8")
xstream.toXML(javaobject, writer)
writer.flush()

zout.closeEntry()
writer.close()
zout.close()

return True

except Exception as e:
print(f"Error saving NEQSim object to ZIP: {e}")
print(f"[save_neqsim] Error saving file: {e}")
return False


def open_neqsim(filename):
"""
Load a NEQSim Java object from a ZIP file, regardless of file extension.
The ZIP must contain a 'process.xml' file.
"""
if not jpype.isJVMStarted():
raise RuntimeError(
"JVM is not started. Please start the JVM with the correct classpath."
)

try:
# Java imports
XStream = jpype.JClass("com.thoughtworks.xstream.XStream")
FileInputStream = jpype.JClass("java.io.FileInputStream")
BufferedInputStream = jpype.JClass("java.io.BufferedInputStream")
ZipInputStream = jpype.JClass("java.util.zip.ZipInputStream")
InputStreamReader = jpype.JClass("java.io.InputStreamReader")
File = jpype.JClass("java.io.File")

# Open ZIP file
file = File(filename)
fin = BufferedInputStream(FileInputStream(file))
zin = ZipInputStream(fin)

# Read the first entry (expected: process.xml)
entry = zin.getNextEntry()
if entry is None:
raise ValueError("ZIP does not contain a valid XML entry.")

reader = InputStreamReader(zin, "UTF-8")

# Deserialize
xstream = XStream()
xstream.allowTypesByWildcard(["neqsim.**"])
javaobject = xstream.fromXML(reader)

zin.close()
return javaobject

except Exception as e:
print(f"[load_neqsim] Failed to deserialize object: {e}")
return None


def save_xml(javaobject, filename):
xstream = jpype.JPackage("com.thoughtworks.xstream")
streamer = xstream.XStream()
Expand Down
5 changes: 5 additions & 0 deletions tests/process/test_ProcessTools.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ def test_gasoilprocess():
process1 = open_neqsim("test_gasoilprocess.zip")
process1.run()

save_neqsim(getProcess(), "test_gasoilprocess.neqsim")

process1 = open_neqsim("test_gasoilprocess.zip")
process1.run()

# assert 3859.9 == approx(recirc1stream.getFlowRate('kg/hr'), abs=1.0)
# assert 22876.1 == approx(pipeloss1st.getOutletStream().getFlowRate("kg/hr"), abs=1.0)
# assert separator3.getGasOutStream().getFlowRate("kg/hr") == pipeloss1st.getOutletStream().getFlowRate("kg/hr")
Expand Down