diff --git a/examples/1D/Continuous/Network1.JSON b/examples/1D/Continuous/Network1.JSON new file mode 100644 index 0000000..811fb48 --- /dev/null +++ b/examples/1D/Continuous/Network1.JSON @@ -0,0 +1,141 @@ +{ +"network": { + "nodes": [ + { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "ground": true + }, + { + "x": 1e-3, + "y": 2e-3, + "z": 0.0, + "ground": false + }, + { + "x": 1e-3, + "y": 1e-3, + "z": 0.0, + "ground": false + }, + { + "x": 1e-3, + "y": 0.0, + "z": 0.0, + "ground": false + }, + { + "x": 2e-3, + "y": 2e-3, + "z": 0.0, + "ground": false + }, + { + "x": 2e-3, + "y": 1e-3, + "z": 0.0, + "ground": false + }, + { + "x": 2e-3, + "y": 0.0, + "z": 0.0, + "ground": false + }, + { + "x": 3e-3, + "y": 1e-3, + "z": 0.0, + "ground": true + } + ], + "channels": [ + { + "node1": 0, + "node2": 1, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 0, + "node2": 2, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 0, + "node2": 3, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 1, + "node2": 4, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 2, + "node2": 5, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 3, + "node2": 5, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 4, + "node2": 5, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 6, + "node2": 5, + "width": 1e-4, + "height": 1e-4 + }, + { + "node1": 5, + "node2": 7, + "width": 1e-4, + "height": 1e-4 + } + ] +}, +"simulation": { + "platform": "continuous", + "type": "1D", + "fluids": [ + { + "name": "Water", + "concentration": 1, + "density": 997, + "viscosity": 0.001 + } + ], + "pumps": [ + { + "channel":0, + "type": "PumpPressure", + "deltaP": 1000 + }, + { + "channel":1, + "type": "PumpPressure", + "deltaP": 1000 + }, + { + "channel":2, + "type": "PumpPressure", + "deltaP": 1000 + } + ], + "settings": { + } +} +} diff --git a/examples/1D/Droplet/Network1.JSON b/examples/1D/Droplet/Network1.JSON new file mode 100644 index 0000000..45c8ea4 --- /dev/null +++ b/examples/1D/Droplet/Network1.JSON @@ -0,0 +1,125 @@ +{ +"network": { + "nodes": [ + { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "ground": true + }, + { + "x": 1e-3, + "y": 0.0, + "z": 0.0, + "ground": false + }, + { + "x": 2e-3, + "y": 0.0, + "z": 0.0, + "ground": false + }, + { + "x": 1e-3, + "y": 5e-4, + "z": 0.0, + "ground": false + }, + { + "x": 2e-3, + "y": 5e-4, + "z": 0.0, + "ground": false + }, + { + "x": 3e-3, + "y": 0.0, + "z": 0.0, + "ground": true + } + ], + "channels": [ + { + "node1": 0, + "node2": 1, + "width": 1e-4, + "height": 3e-5 + }, + { + "node1": 1, + "node2": 2, + "width": 1e-4, + "height": 3e-5 + }, + { + "node1": 1, + "node2": 3, + "width": 1e-4, + "height": 3e-5 + }, + { + "node1": 3, + "node2": 4, + "width": 1e-4, + "height": 3e-5 + }, + { + "node1": 4, + "node2": 2, + "width": 1e-4, + "height": 3e-5 + }, + { + "node1": 2, + "node2": 5, + "width": 1e-4, + "height": 3e-5 + } + ] +}, +"simulation": { + "platform": "droplet", + "type": "1D", + "fluids": [ + { + "name": "Water", + "concentration": 1, + "density": 997, + "viscosity": 0.001 + }, + { + "name": "Oil", + "concentration": 1, + "density": 997, + "viscosity": 0.003 + } + ], + "pumps": [ + { + "channel":0, + "type": "FlowRate", + "flowRate": 3e-11 + } + ], + "fixtures": [ + { + "name": "Setup #1", + "phase": 0, + "bigDropletInjections": [ + { + "fluid": 1, + "volume": 4.5e-13, + "channel": 0, + "pos": 0.5, + "t0": 0, + "deltaT": 0, + "t1": 0 + } + ] + } + ], + "activeFixture": 0, + "settings": { + } +} +} diff --git a/src/architecture/CMakeLists.txt b/src/architecture/CMakeLists.txt index e405888..ead3426 100755 --- a/src/architecture/CMakeLists.txt +++ b/src/architecture/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCE_LIST Channel.hh + ChannelPosition.hh lbmModule.hh Module.hh Network.hh @@ -11,6 +12,7 @@ set(SOURCE_LIST set(HEADER_LIST Channel.h + ChannelPosition.h lbmModule.h Module.h Network.h diff --git a/src/architecture/Channel.h b/src/architecture/Channel.h index 57e5a9a..1d04477 100755 --- a/src/architecture/Channel.h +++ b/src/architecture/Channel.h @@ -8,6 +8,15 @@ namespace arch{ + /** + * @brief An enum to specify the type of channel. + */ + enum class ChannelType { + NORMAL, ///< A normal channel is te regular channel in which flow flows. + BYPASS, ///< A bypass channel allows droplets to bypass another channel, e.g., if a droplet is trapped in that channel. + CLOGGABLE ///< A cloggable channel will be clogged during the time a droplet passes by one of its ends. + }; + /** * @brief An enum to specify the shape of channel. */ @@ -54,20 +63,13 @@ namespace arch{ T area = 0; ///< Area of the channel cross-section in m^2. T pressure = 0; ///< Pressure of a channel in Pa. T channelResistance = 0; ///< Resistance of a channel in Pas/L. - ChannelShape shape = ChannelShape::NONE; ///< The cross-section shape of this channel is rectangular + ChannelShape shape = ChannelShape::NONE; ///< The cross-section shape of this channel is rectangular. + ChannelType type = ChannelType::NORMAL; ///< What kind of channel it is. std::vector>> line_segments; ///< Straight line segments in the channel. std::vector>> arcs; ///< Arcs in the channel. public: - /** - * @brief Constructor of a channel connecting two-nodes. - * @param[in] id Id of the channel. - * @param[in] nodeA Node at one end of the channel. - * @param[in] nodeB Node at the other end of the channel. - */ - Channel(int id, std::shared_ptr> nodeA, std::shared_ptr> nodeB); - /** * @brief Constructor of a rectangular channel with line segments and arcs connecting two-nodes. * @param[in] id Id of the channel. @@ -80,6 +82,14 @@ namespace arch{ std::vector*> line_segments, std::vector*> arcs); + /** + * @brief Constructor of a channel connecting two-nodes. + * @param[in] id Id of the channel. + * @param[in] nodeA Node at one end of the channel. + * @param[in] nodeB Node at the other end of the channel. + */ + Channel(int id, std::shared_ptr> nodeA, std::shared_ptr> nodeB); + /** * @brief Set length of channel. * @param[in] length New length of this channel in m. @@ -99,6 +109,12 @@ namespace arch{ */ void setResistance(T channelResistance); + /** + * @brief Set which kind of channel it is. + * @param[in] channelType Which kind of channel it is. + */ + void setChannelType(ChannelType channelType); + /** * @brief Returns the length of this channel. * @returns Length of channel in m. @@ -131,7 +147,7 @@ namespace arch{ /** * @brief Calculates and returns volume of the channel. - * @returns Volumne of a channel in m^3. + * @returns Volume of a channel in m^3. */ T getVolume() const; @@ -140,6 +156,12 @@ namespace arch{ * @returns What shape the channel has. */ ChannelShape getChannelShape() const; + + /** + * @brief Returns the type of channel. + * @returns What kind of channel it is. + */ + ChannelType getChannelType() const; }; template diff --git a/src/architecture/Channel.hh b/src/architecture/Channel.hh index e9f0d78..30660b5 100755 --- a/src/architecture/Channel.hh +++ b/src/architecture/Channel.hh @@ -104,6 +104,11 @@ namespace arch { this->channelResistance = channelResistance_; } + template + void Channel::setChannelType(ChannelType type_) { + this->type = type_; + } + template T Channel::getLength() const { return length; @@ -134,6 +139,11 @@ namespace arch { return shape; } + template + ChannelType Channel::getChannelType() const { + return type; + } + //===================================================================================== //================================ RectangularChannel ================================ //===================================================================================== diff --git a/src/architecture/ChannelPosition.h b/src/architecture/ChannelPosition.h new file mode 100644 index 0000000..07f5bb2 --- /dev/null +++ b/src/architecture/ChannelPosition.h @@ -0,0 +1,77 @@ +/** + * @file ChannelPosition.h + */ + +#pragma once + +#include "Channel.h" + +namespace arch { + +/** + * @brief Class to specify the boundary position of one end of a droplet. + */ +template +class ChannelPosition { + private: + Channel* channel; ///< Channel in which one end of droplet currently is. + T position; ///< Exact relative position (between 0.0 and 1.0) within the channel. + + public: + /** + * @brief Constructor to create the position of one end of a droplet. + * @param[in] channel Channel in which this end of the droplet currently is. + * @param[in] position Relative position (between 0.0 and 1.0) of the droplet end in this channel. + */ + ChannelPosition(Channel* channel, T position); + + /** + * @brief Change the channel of the channel position (at which one end of the droplet currently is). + * @param[in] channel New channel to which the position should be set. + */ + void setChannel(Channel* const channel); + + /** + * @brief Reset relative position. + * @param[in] position Relative position (between 0.0 and 1.0) within a channel. + */ + void setPosition(T position); + + /** + * @brief Add the volume shift to the current position. + * @param[in] volumeShift Shift of the volume in flow direction in L. + */ + void addToPosition(T volumeShift); + + /** + * @brief Returns pointer to channel in which this end of the droplet currently is. + * @return Pointer to channel at which this end of the droplet currently is. + */ + Channel* getChannel() const; + + /** + * @brief Returns relative position within the channel. + * @return Relative position (between 0.0 and 1.0) at which this end of the droplet currently is. + */ + T getPosition() const; + + /** + * @brief Returns absolute position within channel in m. + * @return Absolute position in m. + */ + T getAbsolutePosition() const; + + /** + * @brief Calculates and returns volume towards node A. + * @return Volume towards node A in L. + */ + T getVolumeA() const; + + /** + * @brief Calculates and returns volume towards node B. + * @return Volume towards node B in L. + */ + T getVolumeB() const; +}; + +} // namespace arch \ No newline at end of file diff --git a/src/architecture/ChannelPosition.hh b/src/architecture/ChannelPosition.hh new file mode 100644 index 0000000..e6f14e3 --- /dev/null +++ b/src/architecture/ChannelPosition.hh @@ -0,0 +1,58 @@ +#include "ChannelPosition.h" + +#include "Channel.h" + +namespace arch { + +template +ChannelPosition::ChannelPosition(Channel* channel, T position) : channel(channel), position(position) {} + +template +void ChannelPosition::setChannel(Channel* const channel) { + this->channel = channel; +} + +template +void ChannelPosition::setPosition(T position) { + // ensure that position stays in range (e.g., due to rounding errors) + if (position < 0.0) { + this->position = 0.0; + } else if (position > 1.0) { + this->position = 1.0; + } else { + this->position = position; + } +} + +template +void ChannelPosition::addToPosition(T volumeShift) { + T newPosition = position + volumeShift / channel->getVolume(); + setPosition(newPosition); +} + +template +Channel* ChannelPosition::getChannel() const { + return channel; +} + +template +T ChannelPosition::getPosition() const { + return position; +} + +template +T ChannelPosition::getAbsolutePosition() const { + return position * channel->getLength(); +} + +template +T ChannelPosition::getVolumeA() const { + return position * channel->getVolume(); +} + +template +T ChannelPosition::getVolumeB() const { + return (1.0 - position) * channel->getVolume(); +} + +} // namespace arch \ No newline at end of file diff --git a/src/architecture/Edge.h b/src/architecture/Edge.h index d790eb9..36eb3f1 100644 --- a/src/architecture/Edge.h +++ b/src/architecture/Edge.h @@ -35,19 +35,19 @@ class Edge { * @brief Get pressure over the edge. * @return Pressure over the edge in Pa. */ - virtual double getPressure() const = 0; + virtual T getPressure() const = 0; /** * @brief Get flow rate in the edge. * @return Flow rate of the edge in m^3/s. */ - virtual double getFlowRate() const = 0; + virtual T getFlowRate() const = 0; /** * @brief Get overall resistance over an edge. * @return Resistance over an edge in Pas/m^3. */ - virtual double getResistance() const = 0; + virtual T getResistance() const = 0; /** * @brief Get pointer to node 0 (node at one end of the edge). diff --git a/src/architecture/Network.h b/src/architecture/Network.h index 6b7b9b2..7d771f2 100755 --- a/src/architecture/Network.h +++ b/src/architecture/Network.h @@ -12,7 +12,6 @@ #include "lbmModule.h" #include "Module.h" #include "Node.h" -#include "Platform.h" #include "PressurePump.h" #include "nlohmann/json.hpp" @@ -66,13 +65,23 @@ template class Network { private: std::unordered_map>> nodes; ///< Nodes the network consists of. + std::set*> sinks; ///< Ids of nodes that are sinks. + std::set*> groundNodes; ///< Ids of nodes that are ground nodes. std::unordered_map>> channels; ///< Map of ids and channel pointers to channels in the network. std::unordered_map>> flowRatePumps; ///< Map of ids and channel pointers to flow rate pumps in the network. std::unordered_map>> pressurePumps; ///< Map of ids and channel pointers to pressure pumps in the network. std::unordered_map>> modules; ///< Map of ids and module pointers to modules in the network. std::unordered_map>> groups; ///< Map of ids and pointers to groups that form the (unconnected) 1D parts of the network - //Platform* platform; ///< The microfluidic platform that operates on this network. + std::unordered_map*>> reach; ///< Set of nodes and corresponding channels (reach) at these nodes in the network. + /** + * @brief Goes through network and sets all nodes and channels that are visited to true. + * @param[in] id Id of the node that is visited. + * @param[in, out] visitedNodes Reference to a map that stores which nodes have already been visited. + * @param[in, out] visitedChannels Reference to a map that stores which channels have already been visited. + */ + void visitNodes(int id, std::unordered_map& visitedNodes, std::unordered_map& visitedChannels); + public: /** * @brief Constructor of the Network @@ -88,13 +97,6 @@ class Network { std::unordered_map>> pressurePump, std::unordered_map>> modules); - public: - /** - * @brief Constructor of the Network that generates a fully connected graph between the nodes. - * @param[in] nodes Nodes of the network. - */ - Network(std::unordered_map>> nodes); - /** * @brief Constructor of the Network * @param[in] nodes Nodes of the network. @@ -103,6 +105,12 @@ class Network { Network(std::unordered_map>> nodes, std::unordered_map>> channels); + /** + * @brief Constructor of the Network that generates a fully connected graph between the nodes. + * @param[in] nodes Nodes of the network. + */ + Network(std::unordered_map>> nodes); + /** * @brief Constructor of the Network from a JSON string * @param json json string @@ -113,32 +121,110 @@ class Network { /** * @brief Adds a new node to the network. */ - int addNode(); + Node* addNode(T x, T y, bool ground=false); /** - * @brief Get a pointer to the node with the specific id. + * @brief Adds a new channel to the chip. + * @param[in] nodeAId Id of the node at one end of the channel. + * @param[in] nodeBId Id of the node at the other end of the channel. + * @param[in] height Height of the channel in m. + * @param[in] width Width of the channel in m. + * @param[in] length Length of the channel in m. + * @param[in] type What kind of channel it is. + * @return Id of the newly created channel. + */ + RectangularChannel* addChannel(int nodeAId, int nodeBId, T height, T width, T length, ChannelType type); + + /** + * @brief Adds a new channel to the chip. + * @param[in] nodeAId Id of the node at one end of the channel. + * @param[in] nodeBId Id of the node at the other end of the channel. + * @param[in] resistance Resistance of the channel in Pas/L. + * @param[in] type What kind of channel it is. + * @return Id of the newly created channel. + */ + RectangularChannel* addChannel(int nodeAId, int nodeBId, T resistance, ChannelType type); + + /** + * @brief Adds a new flow rate pump to the chip. + * @param[in] node0Id Id of the node at one end of the flow rate pump. + * @param[in] node1Id Id of the node at the other end of the flow rate pump. + * @param[in] flowRate Volumetric flow rate of the pump in m^3/s. + * @return Id of the newly created flow rate pump. + */ + FlowRatePump* addFlowRatePump(int nodeAId, int nodeBId, T flowRate); + + /** + * @brief Adds a new pressure pump to the chip. + * @param[in] node0Id Id of the node at one end of the pressure pump. + * @param[in] node1Id Id of the node at the other end of the pressure pump. + * @param[in] pressure Pressure of the pump in Pas/L. + * @return Id of the newly created pressure pump. + */ + PressurePump* addPressurePump(int nodeAId, int nodeBId, T pressure); + + /** + * @brief Adds a new module to the network. */ - std::shared_ptr>& getNode(int nodeId); + int addModule(); /** - * @brief Adds a new channel to the network. + * @brief Checks if a node with the specified id exists in the network. + * @param[in] nodeId Id of the node to check. + * @return If such a node exists. + */ + bool hasNode(int nodeId) const; + + /** + * @brief Specifies a node as sink. + * @param[in] nodeId Id of the node that is a sink. + */ + void setSink(int nodeId); + + /** + * @brief Sets a node as the ground node, i.e., this node has a pressure value of 0 and acts as a reference node for all other nodes. + * @param[in] nodeId Id of the node that should be the ground node of the network. + */ + void setGround(int nodeId); + + /** + * @brief Turns a channel with the specific id into a pressurepump with given pressure. + * @param channelID id of the channel. + * @param pressure pressure value of the pressure pump. */ - int addChannel(); + void setPressurePump(int channelId, T pressure); /** - * @brief Get a pointer to the channel with the specific id. + * @brief Turns a channel with the specific id into a pressurepump with given pressure. + * @param channelID id of the channel. + * @param pressure pressure value of the pressure pump. */ - Channel* getChannel(int channelId) const; + void setFlowRatePump(int channelId, T pressure); /** - * @brief Adds a new module to the network. + * @brief Set the modules of the network for a hybrid simulation. + * @param[in] modules The modules that handle the CFD simulations. */ - int addModule(); + void setModules(std::unordered_map>> modules); /** - * @brief Get a pointer to the module with the specidic id. + * @brief Checks and returns if a node is a sink. + * @param[in] nodeId Id of the node that should be checked. + * @return If the node with the specified id is a sink. + */ + bool isSink(int nodeId) const; + + /** + * @brief Checks and returns if a node is a ground node. + * @param[in] nodeId Id of the node that should be checked. + * @return If the node with the specified id is a ground node. + */ + bool isGround(int nodeId) const; + + /** + * @brief Get a pointer to the node with the specific id. */ - Module* getModule(int moduleId) const; + std::shared_ptr>& getNode(int nodeId); /** * @brief Get the nodes of the network. @@ -146,6 +232,23 @@ class Network { */ const std::unordered_map>>& getNodes() const; + /** + * @brief Returns the id of the ground node. + * @return Id of the ground node. + */ + std::set getGroundIds() const; + + /** + * @brief Returns a pointer to the ground node. + * @return Pointer to the ground node. + */ + std::set*> getGroundNodes() const; + + /** + * @brief Get a pointer to the channel with the specific id. + */ + Channel* getChannel(int channelId) const; + /** * @brief Get the channels of the network. * @returns Channels. @@ -153,11 +256,12 @@ class Network { const std::unordered_map>>& getChannels() const; /** - * @brief Get the modules of the network. - * @returns Modules. - */ - const std::unordered_map>>& getModules() const; - + * @brief Get a map of all channels at a specific node. + * @param[in] nodeId Id of the node at which the adherent channels should be returned. + * @return Vector of pointers to channels adherent to this node. + */ + const std::vector*>& getChannelsAtNode(int nodeId) const; + /** * @brief Get the flow rate pumps of the network. * @returns Flow rate pumps. @@ -171,17 +275,21 @@ class Network { const std::unordered_map>>& getPressurePumps() const; /** - * @brief Get the groups in the network. - * @returns Groups + * @brief Get a pointer to the module with the specidic id. */ - const std::unordered_map>>& getGroups() const; + Module* getModule(int moduleId) const; /** - * @brief Turns a channel with the specific id into a pressurepump with given pressure. - * @param channelID id of the channel. - * @param pressure pressure value of the pressure pump. + * @brief Get the modules of the network. + * @returns Modules. */ - void setPressurePump(int channelId, T pressure); + const std::unordered_map>>& getModules() const; + + /** + * @brief Get the groups in the network. + * @returns Groups + */ + const std::unordered_map>>& getGroups() const; /** * @brief Store the network object in a JSON file. @@ -194,10 +302,10 @@ class Network { void sortGroups(); /** - * @brief Set the modules of the network for a hybrid simulation. - * @param[in] modules The modules that handle the CFD simulations. - */ - void setModules(std::unordered_map>> modules); + * @brief Checks if chip network is valid. + * @return If the network is valid. + */ + bool isNetworkValid(); }; } // namespace arch \ No newline at end of file diff --git a/src/architecture/Network.hh b/src/architecture/Network.hh index be487e4..fda4112 100755 --- a/src/architecture/Network.hh +++ b/src/architecture/Network.hh @@ -151,39 +151,124 @@ namespace arch { } template - int Network::addNode() { - // TODO - std::cerr << "The function addNode() is not implemented." << std::endl; - return -1; + Node* Network::addNode(T x_, T y_, bool ground_) { + int nodeId = nodes.size(); + auto result = nodes.insert({nodeId, std::make_unique>(nodeId, x_, y_, ground_)}); + + if (result.second) { + // insertion happened and we have to add an additional entry into the reach + reach.insert_or_assign(nodeId, std::vector*>{}); + } else { + std::out_of_range( "Could not add Node " + std::to_string(nodeId) + " at (" + std::to_string(x_) + + ", " + std::to_string(y_) + "). Nodes out of bounds."); + } + + // return raw pointer to the node + return result.first->second.get(); } template - std::shared_ptr>& Network::getNode(int nodeId) { - return nodes.at(nodeId); - }; + RectangularChannel* Network::addChannel(int nodeAId, int nodeBId, T height, T width, T length, ChannelType type) { + // create channel + auto nodeA = addNode(nodeAId); + auto nodeB = addNode(nodeBId); + auto id = channels.size() + flowRatePumps.size() + pressurePumps.size(); + auto channel = std::make_unique(id, nodeA, nodeB, height, width, length, type); + + // add to network as long as channel is still a valid pointer + reach.at(nodeA->getId()).push_back(channel.get()); + reach.at(nodeB->getId()).push_back(channel.get()); + + // add channel + channels.insert_or_assign(id, std::move(channel)); + + return id; + } + + template + RectangularChannel* Network::addChannel(int nodeAId, int nodeBId, T resistance, ChannelType type) { + // create channel + auto nodeA = addNode(nodeAId); + auto nodeB = addNode(nodeBId); + auto id = channels.size() + flowRatePumps.size() + pressurePumps.size(); + auto channel = std::make_unique(id, nodeA, nodeB, resistance, type); + + // add to network as long as channel is still a valid pointer + reach.at(nodeA->getId()).push_back(channel.get()); + reach.at(nodeB->getId()).push_back(channel.get()); + + // add channel + channels.insert_or_assign(id, std::move(channel)); + + return id; + } template - int Network::addChannel() { - // TODO - std::cerr << "The function addChannel() is not implemented." << std::endl; - return -1; + FlowRatePump* Network::addFlowRatePump(int nodeAId, int nodeBId, T flowRate) { + // create pump + auto nodeA = addNode(nodeAId); + auto nodeB = addNode(nodeBId); + auto id = channels.size() + flowRatePumps.size() + pressurePumps.size(); + auto pump = std::make_unique(id, nodeA, nodeB, flowRate); + + // add pump + flowRatePumps.insert_or_assign(id, std::move(pump)); + + return id; } template - Channel* Network::getChannel(int channelId) const { - return std::get<0>(channels.at(channelId)); + PressurePump* Network::addPressurePump(int nodeAId, int nodeBId, T pressure) { + // create pump + auto nodeA = addNode(nodeAId); + auto nodeB = addNode(nodeBId); + auto id = channels.size() + flowRatePumps.size() + pressurePumps.size(); + auto pump = std::make_unique(id, nodeA, nodeB, pressure); + + // add pump + pressurePumps.insert_or_assign(id, std::move(pump)); + + return id; } template int Network::addModule() { - // TODO - std::cerr << "The function addModule() is not implemented." << std::endl; - return -1; + /** TODO + * + */ } template - Module* Network::getModule(int moduleId) const { - return std::get<0>(modules.at(moduleId)); + bool Network::hasNode(int nodeId_) const { + return nodes.count(nodeId_); + } + + template + void Network::setSink(int nodeId_) { + nodes.at(nodeId_)->setSink(true); + } + + template + void Network::setGround(int nodeId_) { + nodes.at(nodeId_)->setGround(true); + } + + template + void Network::setPressurePump(int channelId_, T pressure_) { + int nodeAId = channels.at(channelId_).get()->getNodeA(); + int nodeBId = channels.at(channelId_).get()->getNodeB(); + PressurePump* newPump = new PressurePump(channelId_, nodeAId, nodeBId, pressure_); + pressurePumps.try_emplace(channelId_, newPump); + channels.erase(channelId_); + } + + template + void Network::setFlowRatePump(int channelId_, T flowRate_) { + int nodeAId = channels.at(channelId_).get()->getNodeA(); + int nodeBId = channels.at(channelId_).get()->getNodeB(); + FlowRatePump* newPump = new FlowRatePump(channelId_, nodeAId, nodeBId, flowRate_); + flowRatePumps.try_emplace(channelId_, newPump); + channels.erase(channelId_); } template @@ -191,19 +276,62 @@ namespace arch { this->modules = std::move(modules_); } + template + bool Network::isSink(int nodeId_) const { + return nodes.at(nodeId_)->getSink(); + } + + template + bool Network::isGround(int nodeId_) const { + return nodes.at(nodeId_)->getGround(); + } + + template + std::shared_ptr>& Network::getNode(int nodeId) { + return nodes.at(nodeId); + }; + template const std::unordered_map>>& Network::getNodes() const { return nodes; } + template + std::set Network::getGroundIds() const { + if (groundNodes.empty()) { + throw std::invalid_argument("Ground node not defined."); + } + + std::set groundIds; + for (auto groundNode : groundNodes) { + groundIds.insert(groundNode->getId()); + } + + return groundIds; + } + + template + std::set*> Network::getGroundNodes() const { + return groundNodes; + } + + template + Channel* Network::getChannel(int channelId_) const { + return std::get<0>(channels.at(channelId_)); + } + template const std::unordered_map>>& Network::getChannels() const { return channels; } template - const std::unordered_map>>& Network::getModules() const { - return modules; + const std::vector*>& Network::getChannelsAtNode(int nodeId_) const { + try { + return reach.at(nodeId_); + } catch (const std::out_of_range& e) { + throw std::invalid_argument("Node with ID " + std::to_string(nodeId_) + " does not exist."); + } } template @@ -217,23 +345,25 @@ namespace arch { } template - const std::unordered_map>>& Network::getGroups() const { - return groups; + Module* Network::getModule(int moduleId) const { + return std::get<0>(modules.at(moduleId)); } template - void Network::setPressurePump(int channelId_, T pressure_) { - int nodeAId = channels.at(channelId_).get()->getNodeA(); - int nodeBId = channels.at(channelId_).get()->getNodeB(); - PressurePump* newPump = new PressurePump(channelId_, nodeAId, nodeBId, pressure_); - pressurePumps.try_emplace(channelId_, newPump); - channels.erase(channelId_); + const std::unordered_map>>& Network::getModules() const { + return modules; + } + + template + const std::unordered_map>>& Network::getGroups() const { + return groups; } template void Network::toJson(std::string jsonString) const { - // TODO - std::cerr << "The function toJson(std::string jsonString) is not implemented." << std::endl; + /** TODO + * + */ } template @@ -304,4 +434,81 @@ namespace arch { } } + template + bool Network::isNetworkValid() { + // checks if all nodes and channels are connected to ground (if channel network is one graph) + std::unordered_map visitedNodes; + std::unordered_map visitedChannels; + + if (nodes.size() == 0) { + throw std::invalid_argument("No nodes in network."); + } + + for (auto const& [k, v] : channels) { + if (v->getLength() <= 0) { + throw std::invalid_argument("Channel " + std::to_string(k) + ": length is <= 0."); + } + if (v->getHeight() <= 0) { + throw std::invalid_argument("Channel " + std::to_string(k) + ": height is <= 0."); + } + if (v->getWidth() <= 0) { + throw std::invalid_argument("Channel " + std::to_string(k) + ": width is <= 0."); + } + } + + for (auto const& [k, v] : nodes) { + visitedNodes[k] = false; + } + for (auto const& [k, v] : channels) { + visitedChannels[k] = false; + } + + for (auto& node : groundNodes) { + visitNodes(node->getId(), visitedNodes, visitedChannels); + } + + std::string errorNodes = ""; + for (auto const& [k, v] : nodes) { + const auto net = reach.at(k); + int connections = net.size(); + for (auto const& [key, pump] : pressurePumps) { + if (pump->getNode0()->getId() == k || pump->getNode1()->getId() == k) { + connections +=1 ; + } + } + for (auto const& [key, pump] : flowRatePumps) { + if (pump->getNode0()->getId() == k || pump->getNode1()->getId() == k) { + connections +=1 ; + } + } + if (connections <= 1 && !getGroundIds().count(k)) { + errorNodes.append(" " + std::to_string(k)); + } + } + + if (errorNodes.length() != 0) { + throw std::invalid_argument("Chip is invalid. The following nodes are dangling but not ground nodes: " + errorNodes + ". Please set these nodes to ground nodes." ); + return false; + } + + for (auto const& [k, v] : nodes) { + if (visitedNodes[k] == false) { + errorNodes.append(" " + std::to_string(k)); + } + } + std::string errorChannels = ""; + for (auto const& [k, v] : channels) { + if (visitedChannels[k] == false) { + errorChannels.append(" " + std::to_string(k)); + } + } + + if (errorNodes.length() != 0 || errorChannels.length() != 0) { + throw std::invalid_argument("Chip is invalid. The following nodes are not connected to ground: " + errorNodes + ". The following channels are not connected to ground: " + errorChannels); + return false; + } + + return true; + } + } // namespace arch \ No newline at end of file diff --git a/src/architecture/Node.h b/src/architecture/Node.h index 6663d92..b6ca258 100755 --- a/src/architecture/Node.h +++ b/src/architecture/Node.h @@ -22,7 +22,7 @@ namespace arch { * @param[in] x Absolute x position of the node. * @param[in] y Absolute y position of the node. */ - Node(int id, T x, T y); + Node(int id, T x, T y, bool ground=false); /** * @brief Get position of the node. diff --git a/src/architecture/Node.hh b/src/architecture/Node.hh index 2994fb9..2d39054 100755 --- a/src/architecture/Node.hh +++ b/src/architecture/Node.hh @@ -3,8 +3,8 @@ namespace arch { template - Node::Node(int id_, T x_, T y_) : - id(id_) { + Node::Node(int id_, T x_, T y_, bool ground_) : + id(id_), ground(ground_) { pos.push_back(x_); pos.push_back(y_); } diff --git a/src/architecture/Platform.h b/src/architecture/Platform.h deleted file mode 100755 index 228b8a0..0000000 --- a/src/architecture/Platform.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace arch{ - - /** - * @brief An enum to specify the platform of the network - */ - enum class Platform{ - BASE, ///< The base platform for closed channel-based microfluidics - DROPLET ///< The platform for closed channel-based microfluidics with droplets - }; - -} // namespace arch \ No newline at end of file diff --git a/src/baseSimulator.h b/src/baseSimulator.h index 376f529..e415142 100644 --- a/src/baseSimulator.h +++ b/src/baseSimulator.h @@ -6,13 +6,13 @@ #include "nodalAnalysis/NodalAnalysis.h" #include "architecture/Channel.h" +#include "architecture/ChannelPosition.h" #include "architecture/Edge.h" #include "architecture/FlowRatePump.h" #include "architecture/lbmModule.h" #include "architecture/Module.h" #include "architecture/Network.h" #include "architecture/Node.h" -#include "architecture/Platform.h" #include "architecture/PressurePump.h" #include "porting/jsonPorter.h" \ No newline at end of file diff --git a/src/baseSimulator.hh b/src/baseSimulator.hh index 12fc414..e8464d1 100644 --- a/src/baseSimulator.hh +++ b/src/baseSimulator.hh @@ -6,6 +6,7 @@ #include "nodalAnalysis/NodalAnalysis.hh" #include "architecture/Channel.hh" +#include "architecture/ChannelPosition.hh" #include "architecture/Edge.hh" #include "architecture/FlowRatePump.hh" #include "architecture/lbmModule.hh" diff --git a/src/porting/jsonPorter.h b/src/porting/jsonPorter.h index 313977c..179ef8f 100644 --- a/src/porting/jsonPorter.h +++ b/src/porting/jsonPorter.h @@ -10,7 +10,6 @@ #include "../architecture/lbmModule.h" #include "../architecture/Network.h" #include "../architecture/Node.h" -#include "../architecture/Platform.h" #include "../architecture/PressurePump.h" #include "../simulation/Simulation.h" diff --git a/src/porting/jsonPorter.hh b/src/porting/jsonPorter.hh index 93e8226..0ab40bc 100644 --- a/src/porting/jsonPorter.hh +++ b/src/porting/jsonPorter.hh @@ -7,7 +7,6 @@ #include "../architecture/Module.h" #include "../architecture/Network.h" #include "../architecture/Node.h" -#include "../architecture/Platform.h" #include "../simulation/Fluid.h" #include "../simulation/ResistanceModels.h" diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h index 04c5233..7e7cfda 100644 --- a/src/simulation/Simulation.h +++ b/src/simulation/Simulation.h @@ -33,13 +33,13 @@ namespace sim { template class Simulation { private: - // TODO: Add static member variable that keeps track of total memory allocated for lbm sim - Platform platform = Platform::NONE; - Type simType = Type::NONE; - arch::Network* network; ///< Network for which the simulation should be conducted. - ResistanceModel2DPoiseuille* resistanceModel; ///< The resistance model used for te simulation. + // TODO: Add static member variable that keeps track of total memory allocated for lbm sim + Platform platform = Platform::NONE; ///< The microfluidic platform that is simulated in this simulation. + Type simType = Type::NONE; ///< The type of simulation that is being done. + arch::Network* network; ///< Network for which the simulation should be conducted. + ResistanceModel2DPoiseuille* resistanceModel; ///< The resistance model used for te simulation. std::vector>> fluids; - int continuousPhase = 0; ///< Fluid of the continuous phase. + int continuousPhase = 0; ///< Fluid of the continuous phase. /** * @brief Initializes the resistance model and the channel resistances of the empty channels.