Skip to content

Commit 81eca54

Browse files
authored
update dexpi implementation (#1617)
* update dexpi * update
1 parent c411bbf commit 81eca54

File tree

7 files changed

+325
-246
lines changed

7 files changed

+325
-246
lines changed

src/main/java/neqsim/process/processmodel/DexpiXmlReader.java

Lines changed: 77 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import javax.xml.parsers.DocumentBuilder;
1818
import javax.xml.parsers.DocumentBuilderFactory;
1919
import javax.xml.parsers.ParserConfigurationException;
20+
import org.apache.logging.log4j.LogManager;
21+
import org.apache.logging.log4j.Logger;
2022
import org.w3c.dom.Document;
2123
import org.w3c.dom.Element;
2224
import org.w3c.dom.Node;
@@ -31,6 +33,8 @@
3133
* Utility for reading DEXPI XML files and converting them into NeqSim process models.
3234
*/
3335
public final class DexpiXmlReader {
36+
private static final Logger logger = LogManager.getLogger(DexpiXmlReader.class);
37+
3438
private static final Map<String, EquipmentEnum> EQUIPMENT_CLASS_MAP;
3539
private static final Map<String, EquipmentEnum> PIPING_COMPONENT_MAP;
3640

@@ -48,6 +52,11 @@ public final class DexpiXmlReader {
4852
equipmentMap.put("StirredTankReactor", EquipmentEnum.Reactor);
4953
equipmentMap.put("PlugFlowReactor", EquipmentEnum.Reactor);
5054
equipmentMap.put("PackedBedReactor", EquipmentEnum.Reactor);
55+
equipmentMap.put("Column", EquipmentEnum.Column);
56+
equipmentMap.put("Agitator", EquipmentEnum.Mixer);
57+
equipmentMap.put("Boiler", EquipmentEnum.Heater);
58+
equipmentMap.put("Filter", EquipmentEnum.Separator);
59+
equipmentMap.put("Cyclone", EquipmentEnum.Separator);
5160
equipmentMap.put("InlineAnalyzer", EquipmentEnum.Calculator);
5261
equipmentMap.put("GasAnalyzer", EquipmentEnum.Calculator);
5362
equipmentMap.put("Spectrometer", EquipmentEnum.Calculator);
@@ -61,6 +70,14 @@ public final class DexpiXmlReader {
6170
pipingMap.put("PressureSafetyValve", EquipmentEnum.ThrottlingValve);
6271
pipingMap.put("PressureReliefValve", EquipmentEnum.ThrottlingValve);
6372
pipingMap.put("PressureReducingValve", EquipmentEnum.ThrottlingValve);
73+
pipingMap.put("BallValve", EquipmentEnum.ThrottlingValve);
74+
pipingMap.put("GateValve", EquipmentEnum.ThrottlingValve);
75+
pipingMap.put("PlugValve", EquipmentEnum.ThrottlingValve);
76+
pipingMap.put("DiaphragmValve", EquipmentEnum.ThrottlingValve);
77+
pipingMap.put("NeedleValve", EquipmentEnum.ThrottlingValve);
78+
pipingMap.put("OrificePlate", EquipmentEnum.Calculator);
79+
pipingMap.put("FlowMeter", EquipmentEnum.Calculator);
80+
pipingMap.put("RuptureDisk", EquipmentEnum.ThrottlingValve);
6481
PIPING_COMPONENT_MAP = Collections.unmodifiableMap(pipingMap);
6582
}
6683

@@ -72,8 +89,9 @@ private DexpiXmlReader() {}
7289
* @param file DEXPI XML file
7390
* @return a process system populated with units found in the XML
7491
* @throws IOException if the file cannot be read
92+
* @throws DexpiXmlReaderException if the file cannot be parsed
7593
*/
76-
public static ProcessSystem read(File file) throws IOException {
94+
public static ProcessSystem read(File file) throws IOException, DexpiXmlReaderException {
7795
return read(file, null);
7896
}
7997

@@ -86,9 +104,12 @@ public static ProcessSystem read(File file) throws IOException {
86104
* generated piping segments. If {@code null}, a methane/ethane default is used.
87105
* @return a process system populated with units found in the XML
88106
* @throws IOException if the file cannot be read
107+
* @throws DexpiXmlReaderException if the file cannot be parsed
89108
*/
90-
public static ProcessSystem read(File file, Stream templateStream) throws IOException {
109+
public static ProcessSystem read(File file, Stream templateStream)
110+
throws IOException, DexpiXmlReaderException {
91111
Objects.requireNonNull(file, "file");
112+
logger.info("Reading DEXPI XML file: {}", file.getAbsolutePath());
92113
try (InputStream inputStream = new FileInputStream(file)) {
93114
return read(inputStream, templateStream);
94115
}
@@ -100,8 +121,10 @@ public static ProcessSystem read(File file, Stream templateStream) throws IOExce
100121
* @param inputStream stream containing DEXPI XML data
101122
* @return a process system populated with units found in the XML
102123
* @throws IOException if the stream cannot be read
124+
* @throws DexpiXmlReaderException if the stream cannot be parsed
103125
*/
104-
public static ProcessSystem read(InputStream inputStream) throws IOException {
126+
public static ProcessSystem read(InputStream inputStream)
127+
throws IOException, DexpiXmlReaderException {
105128
return read(inputStream, null);
106129
}
107130

@@ -114,9 +137,10 @@ public static ProcessSystem read(InputStream inputStream) throws IOException {
114137
* generated piping segments. If {@code null}, a methane/ethane default is used.
115138
* @return a process system populated with units found in the XML
116139
* @throws IOException if the stream cannot be read
140+
* @throws DexpiXmlReaderException if the stream cannot be parsed
117141
*/
118142
public static ProcessSystem read(InputStream inputStream, Stream templateStream)
119-
throws IOException {
143+
throws IOException, DexpiXmlReaderException {
120144
ProcessSystem processSystem = new ProcessSystem("DEXPI process");
121145
load(inputStream, processSystem, templateStream);
122146
return processSystem;
@@ -128,8 +152,10 @@ public static ProcessSystem read(InputStream inputStream, Stream templateStream)
128152
* @param file XML file to parse
129153
* @param processSystem target process system
130154
* @throws IOException if reading fails
155+
* @throws DexpiXmlReaderException if the file cannot be parsed
131156
*/
132-
public static void load(File file, ProcessSystem processSystem) throws IOException {
157+
public static void load(File file, ProcessSystem processSystem)
158+
throws IOException, DexpiXmlReaderException {
133159
load(file, processSystem, null);
134160
}
135161

@@ -141,10 +167,12 @@ public static void load(File file, ProcessSystem processSystem) throws IOExcepti
141167
* @param templateStream stream providing default fluid, temperature, pressure, and flow rate for
142168
* generated piping segments. If {@code null}, a methane/ethane default is used.
143169
* @throws IOException if reading fails
170+
* @throws DexpiXmlReaderException if the file cannot be parsed
144171
*/
145172
public static void load(File file, ProcessSystem processSystem, Stream templateStream)
146-
throws IOException {
173+
throws IOException, DexpiXmlReaderException {
147174
Objects.requireNonNull(file, "file");
175+
logger.info("Loading DEXPI XML file: {}", file.getAbsolutePath());
148176
try (InputStream inputStream = new FileInputStream(file)) {
149177
load(inputStream, processSystem, templateStream);
150178
}
@@ -156,8 +184,10 @@ public static void load(File file, ProcessSystem processSystem, Stream templateS
156184
* @param inputStream XML input stream
157185
* @param processSystem target process system
158186
* @throws IOException if reading fails
187+
* @throws DexpiXmlReaderException if the stream cannot be parsed
159188
*/
160-
public static void load(InputStream inputStream, ProcessSystem processSystem) throws IOException {
189+
public static void load(InputStream inputStream, ProcessSystem processSystem)
190+
throws IOException, DexpiXmlReaderException {
161191
load(inputStream, processSystem, null);
162192
}
163193

@@ -169,9 +199,10 @@ public static void load(InputStream inputStream, ProcessSystem processSystem) th
169199
* @param templateStream stream providing default fluid, temperature, pressure, and flow rate for
170200
* generated piping segments. If {@code null}, a methane/ethane default is used.
171201
* @throws IOException if reading fails
202+
* @throws DexpiXmlReaderException if the stream cannot be parsed
172203
*/
173204
public static void load(InputStream inputStream, ProcessSystem processSystem,
174-
Stream templateStream) throws IOException {
205+
Stream templateStream) throws IOException, DexpiXmlReaderException {
175206
Objects.requireNonNull(inputStream, "inputStream");
176207
Objects.requireNonNull(processSystem, "processSystem");
177208

@@ -182,12 +213,13 @@ public static void load(InputStream inputStream, ProcessSystem processSystem,
182213

183214
Stream streamTemplate = templateOrDefault(templateStream);
184215

185-
addEquipmentUnits(document, processSystem);
186-
addPipingComponents(document, processSystem);
216+
addUnits(document, processSystem, "Equipment", EQUIPMENT_CLASS_MAP, DexpiMetadata.TAG_NAME);
217+
addUnits(document, processSystem, "PipingComponent", PIPING_COMPONENT_MAP,
218+
"PipingComponentNumberAssignmentClass");
187219
addPipingSegments(document, processSystem, streamTemplate);
188220
}
189221

190-
private static Document parseDocument(InputStream inputStream) throws IOException {
222+
private static Document parseDocument(InputStream inputStream) throws DexpiXmlReaderException {
191223
try {
192224
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
193225
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
@@ -203,56 +235,54 @@ private static Document parseDocument(InputStream inputStream) throws IOExceptio
203235
Document document = builder.parse(inputStream);
204236
document.getDocumentElement().normalize();
205237
return document;
206-
} catch (ParserConfigurationException | SAXException | IllegalArgumentException e) {
207-
throw new IOException("Unable to parse DEXPI XML", e);
238+
} catch (ParserConfigurationException | SAXException | IOException
239+
| IllegalArgumentException e) {
240+
throw new DexpiXmlReaderException("Unable to parse DEXPI XML", e);
208241
}
209242
}
210243

211-
private static void addEquipmentUnits(Document document, ProcessSystem processSystem) {
212-
NodeList equipmentNodes = document.getElementsByTagName("Equipment");
213-
for (int i = 0; i < equipmentNodes.getLength(); i++) {
214-
Node node = equipmentNodes.item(i);
215-
if (node.getNodeType() != Node.ELEMENT_NODE) {
216-
continue;
217-
}
218-
Element element = (Element) node;
219-
String componentClass = element.getAttribute("ComponentClass");
220-
EquipmentEnum equipmentEnum = EQUIPMENT_CLASS_MAP.get(componentClass);
221-
if (equipmentEnum == null) {
244+
private static void addUnits(Document document, ProcessSystem processSystem, String tagName,
245+
Map<String, EquipmentEnum> equipmentMap, String nameAttribute) {
246+
NodeList parentNodes = document.getElementsByTagName(tagName);
247+
logger.info("Found {} {} parent elements", parentNodes.getLength(), tagName);
248+
249+
int totalUnits = 0;
250+
for (int i = 0; i < parentNodes.getLength(); i++) {
251+
Node parentNode = parentNodes.item(i);
252+
if (parentNode.getNodeType() != Node.ELEMENT_NODE) {
222253
continue;
223254
}
255+
Element parentElement = (Element) parentNode;
224256

225-
String baseName = firstNonEmpty(attributeValue(element, DexpiMetadata.TAG_NAME),
226-
element.getAttribute("ID"));
227-
addDexpiUnit(processSystem, element, equipmentEnum, baseName,
228-
element.getAttribute("ComponentClass"));
229-
}
230-
}
257+
// Look for all child elements of the parent (Equipment or PipingComponent)
258+
NodeList childNodes = parentElement.getChildNodes();
259+
for (int j = 0; j < childNodes.getLength(); j++) {
260+
Node childNode = childNodes.item(j);
261+
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
262+
continue;
263+
}
264+
Element element = (Element) childNode;
265+
String componentClass = element.getAttribute("ComponentClass");
266+
EquipmentEnum equipmentEnum = equipmentMap.get(componentClass);
267+
if (equipmentEnum == null) {
268+
logger.warn("Unsupported component class: {}", componentClass);
269+
continue;
270+
}
231271

232-
private static void addPipingComponents(Document document, ProcessSystem processSystem) {
233-
NodeList componentNodes = document.getElementsByTagName("PipingComponent");
234-
for (int i = 0; i < componentNodes.getLength(); i++) {
235-
Node node = componentNodes.item(i);
236-
if (node.getNodeType() != Node.ELEMENT_NODE) {
237-
continue;
272+
String baseName = firstNonEmpty(attributeValue(element, nameAttribute),
273+
attributeValue(element, DexpiMetadata.TAG_NAME), element.getAttribute("ID"));
274+
addDexpiUnit(processSystem, element, equipmentEnum, baseName,
275+
element.getAttribute("ComponentClass"));
276+
totalUnits++;
238277
}
239-
Element element = (Element) node;
240-
EquipmentEnum equipmentEnum = PIPING_COMPONENT_MAP.get(element.getAttribute("ComponentClass"));
241-
if (equipmentEnum == null) {
242-
continue;
243-
}
244-
245-
String baseName = firstNonEmpty(
246-
attributeValue(element, "PipingComponentNumberAssignmentClass"),
247-
attributeValue(element, DexpiMetadata.TAG_NAME), element.getAttribute("ID"));
248-
addDexpiUnit(processSystem, element, equipmentEnum, baseName,
249-
element.getAttribute("ComponentClass"));
250278
}
279+
logger.info("Added {} units from {} elements", totalUnits, tagName);
251280
}
252281

253282
private static void addPipingSegments(Document document, ProcessSystem processSystem,
254283
Stream templateStream) {
255284
NodeList segmentNodes = document.getElementsByTagName("PipingNetworkSegment");
285+
logger.info("Found {} PipingNetworkSegments", segmentNodes.getLength());
256286
for (int i = 0; i < segmentNodes.getLength(); i++) {
257287
Node node = segmentNodes.item(i);
258288
if (node.getNodeType() != Node.ELEMENT_NODE) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package neqsim.process.processmodel;
2+
3+
/**
4+
* Exception thrown when there is an error reading a DEXPI XML file.
5+
*/
6+
public class DexpiXmlReaderException extends Exception {
7+
8+
private static final long serialVersionUID = 1L;
9+
10+
public DexpiXmlReaderException(String message) {
11+
super(message);
12+
}
13+
14+
public DexpiXmlReaderException(String message, Throwable cause) {
15+
super(message, cause);
16+
}
17+
}

src/main/java/neqsim/process/processmodel/DexpiXmlWriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,4 +367,4 @@ private static void writeDocument(Document document, OutputStream outputStream)
367367
throw new IOException("Unable to configure XML transformer", e);
368368
}
369369
}
370-
}
370+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package neqsim.process.processmodel;
2+
3+
import java.io.IOException;
4+
import javax.xml.XMLConstants;
5+
import javax.xml.parsers.DocumentBuilder;
6+
import javax.xml.parsers.DocumentBuilderFactory;
7+
import javax.xml.parsers.ParserConfigurationException;
8+
9+
/**
10+
* Utility class for XML parsing and writing.
11+
*/
12+
public final class XmlUtil {
13+
14+
private XmlUtil() {}
15+
16+
/**
17+
* Creates a new DocumentBuilder with secure processing features enabled.
18+
*
19+
* @return a new DocumentBuilder
20+
* @throws IOException if a parser configuration exception occurs
21+
*/
22+
public static DocumentBuilder createDocumentBuilder() throws IOException {
23+
try {
24+
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
25+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
26+
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
27+
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
28+
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
29+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
30+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
31+
factory.setNamespaceAware(false);
32+
factory.setExpandEntityReferences(false);
33+
factory.setXIncludeAware(false);
34+
return factory.newDocumentBuilder();
35+
} catch (ParserConfigurationException e) {
36+
throw new IOException("Unable to create XML document builder", e);
37+
}
38+
}
39+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# DEXPI Equipment Class to NeqSim EquipmentEnum Mapping
2+
PlateHeatExchanger=HeatExchanger
3+
ShellAndTubeHeatExchanger=HeatExchanger
4+
TubularHeatExchanger=HeatExchanger
5+
AirCooledHeatExchanger=HeatExchanger
6+
FiredHeater=Heater
7+
ElectricHeater=Heater
8+
Cooler=Cooler
9+
CentrifugalPump=Pump
10+
ReciprocatingPump=Pump
11+
CentrifugalCompressor=Compressor
12+
ReciprocatingCompressor=Compressor
13+
Fan=Compressor
14+
Blower=Compressor
15+
Tank=Tank
16+
Vessel=Tank
17+
StirredTankReactor=Reactor
18+
PlugFlowReactor=Reactor
19+
PackedBedReactor=Reactor
20+
Column=Column
21+
Agitator=Mixer
22+
Boiler=Heater
23+
Filter=Separator
24+
Cyclone=Separator
25+
Separator=Separator
26+
Expander=Expander
27+
Turbine=Expander
28+
InlineAnalyzer=Calculator
29+
GasAnalyzer=Calculator
30+
Spectrometer=Calculator
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# DEXPI Piping Component Class to NeqSim EquipmentEnum Mapping
2+
GlobeValve=ThrottlingValve
3+
ButterflyValve=ThrottlingValve
4+
CheckValve=ThrottlingValve
5+
ControlValve=ThrottlingValve
6+
PressureSafetyValve=ThrottlingValve
7+
PressureReliefValve=ThrottlingValve
8+
PressureReducingValve=ThrottlingValve
9+
BallValve=ThrottlingValve
10+
GateValve=ThrottlingValve
11+
PlugValve=ThrottlingValve
12+
DiaphragmValve=ThrottlingValve
13+
NeedleValve=ThrottlingValve
14+
OrificePlate=Calculator
15+
FlowMeter=Calculator
16+
RuptureDisk=ThrottlingValve

0 commit comments

Comments
 (0)