Intersect and Interface Finding
Example
This module contains a class InterfaceFinder
for finding the interface between two or more fluids.
This is achieved by finding the location where the two densities are equal.
To ensure that no false interfaces are found, the dataset should mask the points where the density is exactly zero.
All data frames must also have the exact same coordinate grid (i.e. same start, end and spacing).
import thermotar as th
from thermotar.intersects import InterfaceFinder, multi_intersect, PairIntersectFinders
def _is_zero_density(df):
"""
Helper function that masking zero density.
Using a helper allows us to load and mask in one line easily.
"""
return df["density_number"] == 0.0
def _is_left(df):
return df["Coord1"] < 0.0
# Load in the data for each section
# and mask where the densities are zero
# Masking ensures intersection isn't found where both have zero density
# Also masking the left interface, can only do one at a time.
df_au = (
th.create_chunk("./resources/gold.chunk").data.mask(_is_zero_density).mask(_is_left)
)
df_ligand = (
th.create_chunk("./resources/ligand.chunk")
.data.mask(_is_zero_density)
.mask(_is_left)
)
# For simplicity just using oxygen atoms.
# Ideally should use oxygen + hydrogen.
df_water = (
th.create_chunk("./resources/oxygen.chunk")
.data.mask(_is_zero_density)
.mask(_is_left)
)
The InterfaceFinder
class takes in a list of DataFrames to find interfaces between and the label of the spatial coordinate.
This list of DataFrames must be in the order that the components appear spatially.
For components A, B and C, must be in the order [df_A,df_B,df_C]
, if you want to find the interfaces between A and B and B and C.
# Intersections
# Data frames must be in spatial order.
inter_finder = InterfaceFinder(
[df_au, df_ligand, df_water], "Coord1", y_coord="density_number"
)
The constructor automatically finds the intersects and creates masked DataFrames.
There are a variety of attributes and convinence methods,
# Locations of the intersects
print(inter_finder.intersects)
# Masked Dataframes
print(inter_finder.masked_dfs)
# A method for plotting the data
# These plots aren't very pretty, but good for debugging
inter_finder.make_plots(["density_number", "temp"], show_original=True)
plt.show()
# Extrapolate the temperatures to the interfaces
print(inter_finder.interface_values(y_props="temp"))
# Estimate the temperature jumps at the interfaces
print(inter_finder.deltas(y_props="temp"))
If you don't care about all these methods and just want the raw interface locations, you can just use multi_intersect
, which is what is used internally by InterfaceFinder
# You can alternatively just get the interface locations using
# note, the y_column is required now
intersects = multi_intersect([df_au, df_ligand, df_water], "Coord1", "density_number")
Or if you only have the one interface, you may prefer the pair methods used internally by multi intersect
# And if you only care about one interface, you can use the `PairIntersect`s directly
PairIntersectFinders.interp(df_ligand, df_water, "Coord1", "density_number")
Reference
InterfaceFinder
dataclass
Find the interfaces between several DataFrames
Create using:
Access the interface locations using: Extrapolate the properties to the interface using: Attributes: Required: dataframes: List of DataFrames to find interfaces between x_coord: Name of the x coordinate columnOptional:
y_coord="density_number": Name of the y coordinate column to find the intersects between
intersect_method: Method to use to find the intersects. See `multi_intersect` for options
intersect_kwargs: Keyword arguments to pass to the intersect method
pad_masks: Amount to pad the masks by. If None, no padding is applied
intersects: List of the intersect locations, calculated in `__post_init__`
bounds: List of the bounds of the masks, calculated in `__post_init__`
masks: List of the masks, calculated in `__post_init__`
masked_dfs: List of the masked DataFrames, calculated in `__post_init__`
Source code in thermotar/intersects.py
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
|
interface_values(fit_range=5, y_props=None, n=1, return_fits=False, group_by_interface=False, error_suffix=None)
Find the values extrapolated to the interfaces
Source code in thermotar/intersects.py
PairIntersectFinders
Namespace collection of methods for find the intersects between DataFrames
`coarse`: Finds the interface crudely. Finds the index of the row with the
smallest absolute difference between the y_property
`interp`: Interpolates the difference in the y property first, then uses fsolve
to find a more precise intersection. Uses `coarse` for a first guess.
Source code in thermotar/intersects.py
coarse(df_a, df_b, x_prop, y_prop)
staticmethod
Find the intersect between a pair of DataFrames. This uses a crude approximation, it finds the index in both DataFrames where df_a[y_prop]-df_b[y_prop] is at it's smallest absolute value and then returns the x_prop value at this index.
Source code in thermotar/intersects.py
bounds_from_intersects(intersects)
Find the limits either side of each dataframe. TODO: Check that the functions that call this have the right signature
Source code in thermotar/intersects.py
extrapolate_to_interface(df_a, x_intersect, x_prop, fit_range=5, return_coeffs=True, return_fits=False, y_props=None, error_suffix=None, n=1)
Return a row extrapolated with a polynomial of order n
to the interface, for a single DataFrame.
df_a: pd.DataFrame = DataFrame to extrapolate to interface fit_range: float = Range either side of the interface to fit to. x_intersect: float = x coordinate of the interface x_prop: str = Name of x coordinate y_props: List[str] = List of columns to fit to. If None, fit to all columns.
error_suffix : str = Suffix of error column to use for weighting. If None, no weighting is used. TODO: Currently not working correctly
returns a dataframe row with the order TODO: add weightings by errors TODO: Neaten weighting by errors code to handle missing columns. TODO: Weighting can only currently be used on columns when not None
Source code in thermotar/intersects.py
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
|
interface_values(dfs, bounds, x_prop, fit_range=5, y_props=None, n=1, return_fits=False, group_by_interface=False, error_suffix=None)
For all DataFrames in dfs
, extrapolate to the interface defined by bounds
.
Source code in thermotar/intersects.py
masks_from_intersects(dfs, intersects, x_coord, padding=None)
From a list of intersects, create masks for the data
Creates selections for each DataFrame that are either side of the intersect. Does the same work as bounds_from_intersects first, but then creates masks for each DataFrame.
dfs: List[pd.DataFrame] = List of DataFrames to find intersects of. Must be in the order they appear spatially intersects: List[float] = List of intersects to create masks from x_coord: str = Name of x coordinate to find intersect in padding: float = Amount to pad the masks by. Default is 0.0
Source code in thermotar/intersects.py
multi_intersect(dfs, x_prop, y_prop, intersect_method='linear', **intersect_kwargs)
Insert a list of N DataFrames and find the (N-1) intersects of neighbouring data frames in the lists.
dfs: List[pd.DataFrame] = List of DataFrames to find intersects of. Must be in the order they appear spatially
x_prop: str = Name of x coordinate to find intersect in y_prop : str = Name of column to use to find intersections with.
intersect_method: 'interp' : interpolates the data sets, by default linearly 'linear' : interpolates the data points linearly 'cubic' : interpolates the data points cubically to find intersects. TODO: 'spline'