Compare commits
24 Commits
add_ai_rev
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c95fa0ed80 | |||
| 382bbc7689 | |||
| 6e8a95b056 | |||
| 29dc178ec4 | |||
| 5785abd22e | |||
| 0ab41e7c0b | |||
| fff417b041 | |||
| 5c6a86815d | |||
| 0e64fe9cf5 | |||
| bbd57d0344 | |||
| 849fc5de11 | |||
| b679ccfbe6 | |||
| 393a909eb2 | |||
| 33cdd99dde | |||
| a433d213f2 | |||
| 36f0e16bce | |||
| 516c36c564 | |||
| b28e6a033e | |||
| b97a34a642 | |||
| 6e94041482 | |||
| 9373d31dbc | |||
| 5d998bd24b | |||
| b88213f200 | |||
| f1f43abea8 |
@@ -1,5 +1,6 @@
|
||||
"""Code Reviewer for Gitea."""
|
||||
|
||||
import asyncio
|
||||
import fnmatch
|
||||
import json
|
||||
import os
|
||||
@@ -7,6 +8,7 @@ import re
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
import aiohttp
|
||||
from model import Model
|
||||
|
||||
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "")
|
||||
@@ -57,8 +59,8 @@ def parse_diff(diff: str) -> list[dict[str, Any]]:
|
||||
r"(?s)diff --git a/(.+?) b/(.*?)\r?\n(.*?)(?=diff --git a/|$)", re.S
|
||||
)
|
||||
old_new_pattern = re.compile(r"(?m)^(---|\+\+\+)\s+(.*)$")
|
||||
hunk_pattern = re.compile(
|
||||
r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*?)(?=^@@ |$)",
|
||||
chunk_range_pattern = re.compile(
|
||||
r"@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*?)?(?=@@|\Z)",
|
||||
re.MULTILINE | re.DOTALL,
|
||||
)
|
||||
list_diff = []
|
||||
@@ -77,33 +79,31 @@ def parse_diff(diff: str) -> list[dict[str, Any]]:
|
||||
print("Neglict deleted file")
|
||||
continue
|
||||
new_file = new_file.lstrip("b/")
|
||||
|
||||
hunk_match = hunk_pattern.search(diff_text)
|
||||
if hunk_match is None:
|
||||
continue
|
||||
old_idx = int(hunk_match.group(1))
|
||||
new_idx = int(hunk_match.group(3))
|
||||
remain_text = diff_text[hunk_match.end() + 1 :]
|
||||
diff_text = []
|
||||
for line in remain_text.splitlines():
|
||||
if line.startswith("-"):
|
||||
diff_text.append(f"{old_idx} {line}")
|
||||
old_idx += 1
|
||||
elif line.startswith("+"):
|
||||
diff_text.append(f"{new_idx} {line}")
|
||||
new_idx += 1
|
||||
else:
|
||||
diff_text.append(line)
|
||||
diff_text = "\n".join(diff_text)
|
||||
|
||||
if any(fnmatch.fnmatch(new_file, pattern) for pattern in EXCLUDE_PATTERNS):
|
||||
print(f"Exclude file {new_file}")
|
||||
continue
|
||||
|
||||
output_diff_text = []
|
||||
for chunk_range_match in chunk_range_pattern.finditer(diff_text):
|
||||
old_idx = int(chunk_range_match.group(1))
|
||||
new_idx = int(chunk_range_match.group(3))
|
||||
for line in chunk_range_match.group(5).splitlines():
|
||||
if line.startswith("-"):
|
||||
output_diff_text.append(f"{old_idx} None {line}")
|
||||
old_idx += 1
|
||||
elif line.startswith("+"):
|
||||
output_diff_text.append(f"None {new_idx} {line}")
|
||||
new_idx += 1
|
||||
else:
|
||||
output_diff_text.append(f"{old_idx} {new_idx} {line}")
|
||||
old_idx += 1
|
||||
new_idx += 1
|
||||
|
||||
output_diff_text = "\n".join(output_diff_text)
|
||||
list_diff.append(
|
||||
{
|
||||
"file": new_file,
|
||||
"chunk": diff_text,
|
||||
"chunk": output_diff_text,
|
||||
}
|
||||
)
|
||||
return list_diff
|
||||
@@ -133,7 +133,7 @@ def create_comment(
|
||||
return comments
|
||||
|
||||
|
||||
def analyze_single_chunks(
|
||||
async def analyze_single_chunks(
|
||||
single_chunk_model: Model, parsed_diff: list[dict[str, Any]]
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Analyze single chunks and create comments.
|
||||
@@ -145,29 +145,33 @@ def analyze_single_chunks(
|
||||
Returns:
|
||||
list[dict[str, Any]]: comments for single chunk review
|
||||
"""
|
||||
comments = []
|
||||
title = EVENT_DATA["pull_request"]["title"]
|
||||
description = EVENT_DATA["pull_request"]["body"]
|
||||
for diff in parsed_diff:
|
||||
|
||||
async def process_single_chunk(diff: dict[str, Any]):
|
||||
file = diff["file"]
|
||||
chunk = diff["chunk"]
|
||||
response = single_chunk_model.get_response_single_chunk(
|
||||
response = await single_chunk_model.get_response_single_chunk(
|
||||
file, title, description, chunk
|
||||
)
|
||||
response = response.strip("`").lstrip("json").strip() or "[]"
|
||||
|
||||
try:
|
||||
response_json = json.loads(response)
|
||||
new_comments = create_comment(file, response_json)
|
||||
comments.extend(new_comments)
|
||||
return create_comment(file, response_json)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Failed to parse response: {response}")
|
||||
continue
|
||||
return []
|
||||
|
||||
title = EVENT_DATA["pull_request"]["title"]
|
||||
description = EVENT_DATA["pull_request"]["body"]
|
||||
tasks = [process_single_chunk(diff) for diff in parsed_diff]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# Flatten the list of comments
|
||||
comments = [comment for result in results for comment in result]
|
||||
return comments
|
||||
|
||||
|
||||
def get_file_content(file: str) -> str | None:
|
||||
async def get_file_content(file: str) -> str | None:
|
||||
"""Get file content from Gitea.
|
||||
|
||||
Args:
|
||||
@@ -183,15 +187,18 @@ def get_file_content(file: str) -> str | None:
|
||||
url = f"{repo_url}/raw/{branch}%2F{replaced_file}?ref={branch}"
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
response.raise_for_status()
|
||||
return response.text
|
||||
except requests.RequestException as e:
|
||||
print(f"Failed to get file content: {e}")
|
||||
return None
|
||||
async with aiohttp.ClientSession(headers=HEADERS) as session:
|
||||
async with session.get(url) as response:
|
||||
response.raise_for_status()
|
||||
return await response.text()
|
||||
except aiohttp.ClientError as e: # More specific exception handling
|
||||
print(f"Network error fetching {file}: {e}")
|
||||
except asyncio.TimeoutError:
|
||||
print(f"Timeout fetching {file}")
|
||||
return None
|
||||
|
||||
|
||||
def analyze_full_context(
|
||||
async def analyze_full_context(
|
||||
full_context_model: Model, parsed_diff: list[dict[str, Any]]
|
||||
) -> str:
|
||||
"""Analyze full context and create review.
|
||||
@@ -203,20 +210,26 @@ def analyze_full_context(
|
||||
Returns:
|
||||
str: review for full context
|
||||
"""
|
||||
file_contents = []
|
||||
for diff in parsed_diff:
|
||||
|
||||
async def get_file_data(diff: dict[str, Any]):
|
||||
file = diff["file"]
|
||||
chunk = diff["chunk"]
|
||||
content = get_file_content(file)
|
||||
if content is None:
|
||||
continue
|
||||
file_contents.append(f"File: {file}")
|
||||
file_contents.append(content)
|
||||
file_contents.append(f"Diff: {chunk}")
|
||||
return None
|
||||
return f"File: {file}\n{content}\nDiff: {chunk}"
|
||||
|
||||
tasks = [get_file_data(diff) for diff in parsed_diff]
|
||||
file_contents_list = await asyncio.gather(*tasks)
|
||||
|
||||
file_contents = [item for item in file_contents_list if item is not None]
|
||||
|
||||
if not file_contents:
|
||||
return ""
|
||||
|
||||
title = EVENT_DATA["pull_request"]["title"]
|
||||
description = EVENT_DATA["pull_request"]["body"]
|
||||
response = full_context_model.get_response_full_context(
|
||||
response = await full_context_model.get_response_full_context(
|
||||
title, description, file_contents
|
||||
)
|
||||
response = response.strip("`").lstrip("markdown").strip()
|
||||
@@ -246,10 +259,10 @@ def post_review(
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Code Reviewer for Gitea."""
|
||||
async def main() -> None:
|
||||
"""Code Reviewer for Gitea: Asynchronous version."""
|
||||
if EVENT_DATA["action"] not in ["opened", "synchronized"]:
|
||||
print("Unsupproted event.")
|
||||
print("Unsupported event.")
|
||||
return
|
||||
|
||||
diff = get_diff()
|
||||
@@ -271,10 +284,21 @@ def main() -> None:
|
||||
)
|
||||
|
||||
parsed_diff = parse_diff(diff)
|
||||
comments = analyze_single_chunks(single_chunk_model, parsed_diff)
|
||||
full_context_response = analyze_full_context(full_context_model, parsed_diff)
|
||||
comments_task = asyncio.create_task(
|
||||
analyze_single_chunks(single_chunk_model, parsed_diff)
|
||||
)
|
||||
|
||||
if EVENT_DATA["action"] == "opened":
|
||||
full_context_response_task = asyncio.create_task(
|
||||
analyze_full_context(full_context_model, parsed_diff)
|
||||
)
|
||||
full_context_response = await full_context_response_task
|
||||
else:
|
||||
full_context_response = ""
|
||||
|
||||
comments = await comments_task
|
||||
post_review(full_context_response, comments)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -4,8 +4,16 @@ from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
import google.generativeai as genai
|
||||
from anthropic import Anthropic
|
||||
from openai import OpenAI
|
||||
import typing_extensions as typing
|
||||
from anthropic import AsyncAnthropic
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
|
||||
class GoogleResponse(typing.TypedDict):
|
||||
"""The response from Google model."""
|
||||
|
||||
lineNumber: int
|
||||
reviewComment: str
|
||||
|
||||
|
||||
class ModelProvider(Enum):
|
||||
@@ -35,6 +43,7 @@ class ModelProvider(Enum):
|
||||
PREFIX_TO_MODEL = {
|
||||
"gpt": ModelProvider.OPENAI,
|
||||
"o1": ModelProvider.OPENAI,
|
||||
"o3": ModelProvider.OPENAI,
|
||||
"claude": ModelProvider.ANTHROPIC,
|
||||
"gemini": ModelProvider.GOOGLE,
|
||||
"deepseek": ModelProvider.DEEPSEEK,
|
||||
@@ -79,16 +88,18 @@ class Model:
|
||||
"""
|
||||
match self.provider:
|
||||
case ModelProvider.OPENAI:
|
||||
return OpenAI(api_key=api_key)
|
||||
return AsyncOpenAI(api_key=api_key)
|
||||
case ModelProvider.ANTHROPIC:
|
||||
return Anthropic(api_key=api_key)
|
||||
return AsyncAnthropic(api_key=api_key)
|
||||
case ModelProvider.GOOGLE:
|
||||
genai.configure(api_key=api_key)
|
||||
return genai.GenerativeModel(model=self.model, api_key=api_key)
|
||||
return genai.GenerativeModel(
|
||||
model_name=self.model, system_instruction=self.system_prompt
|
||||
)
|
||||
case ModelProvider.DEEPSEEK:
|
||||
return OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
|
||||
return AsyncOpenAI(api_key=api_key, base_url="https://api.deepseek.com")
|
||||
|
||||
def request(self, prompt: str) -> str:
|
||||
async def request(self, prompt: str) -> str:
|
||||
"""Request the model to generate a response.
|
||||
|
||||
Args:
|
||||
@@ -99,7 +110,7 @@ class Model:
|
||||
"""
|
||||
match self.provider:
|
||||
case ModelProvider.OPENAI | ModelProvider.DEEPSEEK:
|
||||
response = self.session.chat.completions.create(
|
||||
response = await self.session.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{"role": "system", "content": self.system_prompt},
|
||||
@@ -113,7 +124,7 @@ class Model:
|
||||
)
|
||||
return response.choices[0].message.content.strip()
|
||||
case ModelProvider.ANTHROPIC:
|
||||
response = self.session.messages.create(
|
||||
response = await self.session.messages.create(
|
||||
model=self.model,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
system=[
|
||||
@@ -128,10 +139,16 @@ class Model:
|
||||
)
|
||||
return response.content[0].text.strip()
|
||||
case ModelProvider.GOOGLE:
|
||||
response = self.session.generate_content(prompt)
|
||||
response = await self.session.generate_content_async(
|
||||
prompt,
|
||||
generation_config=genai.GenerationConfig(
|
||||
response_mime_type="application/json",
|
||||
response_schema=list[GoogleResponse],
|
||||
),
|
||||
)
|
||||
return response.text.strip()
|
||||
|
||||
def get_response_single_chunk(
|
||||
async def get_response_single_chunk(
|
||||
self, file: str, title: str, description: str, chunk: str
|
||||
) -> str:
|
||||
"""Get the response for a single chunk.
|
||||
@@ -146,9 +163,9 @@ class Model:
|
||||
str: The response.
|
||||
"""
|
||||
prompt = SINGLE_CHUNK_USER_PROMPT.format(file, title, description, chunk)
|
||||
return self.request(prompt)
|
||||
return await self.request(prompt)
|
||||
|
||||
def get_response_full_context(
|
||||
async def get_response_full_context(
|
||||
self, title: str, description: str, file_contents: list[str]
|
||||
) -> str:
|
||||
"""Get the response for full context.
|
||||
@@ -165,7 +182,7 @@ class Model:
|
||||
prompt = FULL_CONTEXT_USER_PROMPT.format(
|
||||
title, description, "\n".join(file_contents)
|
||||
)
|
||||
return self.request(prompt)
|
||||
return await self.request(prompt)
|
||||
except Exception as e:
|
||||
print(f"Error during full context response: {e}")
|
||||
print(prompt)
|
||||
@@ -175,14 +192,21 @@ class Model:
|
||||
SINGLE_CHUNK_SYSTEM_PROMPT = (
|
||||
"Your task is to review pull requests. Instructions:\n"
|
||||
"- Provide the response in the following JSON format: "
|
||||
"""[{{"lineNumber": <line_number>, "reviewComment": "<review comment>"}}] \n"""
|
||||
"""[{{"lineNumber": int, "reviewComment": str}}] \n"""
|
||||
"- lineNumber is about the line number of the code that in new file. \n"
|
||||
"- lineNumber can be found at the front of each line. \n"
|
||||
"- At the first number is old line number, the second number is new line number. \n"
|
||||
"- If the line starts with `+`, it means the line is added. \n"
|
||||
"- If the line starts with `-`, it means the line is deleted. \n"
|
||||
"- Evaluate whether the code changes and additions are appropriate "
|
||||
"and if the new code structure is suitable. \n"
|
||||
"- Do not give positive comments or compliments. \n"
|
||||
"- Provide comments and suggestions ONLY if there is something to improve"
|
||||
"otherwise return an empty array. \n"
|
||||
"- Write the comment in GitHub Markdown format. \n"
|
||||
"- Use the given description only for the overall context "
|
||||
"and only comment the code. \n"
|
||||
"- Do not suggest type hint or naming convention. \n"
|
||||
"- IMPORTANT: NEVER suggest adding comments to the code. \n"
|
||||
)
|
||||
SINGLE_CHUNK_USER_PROMPT = (
|
||||
|
||||
@@ -21,15 +21,15 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests py-gitea openai anthropic google-generativeai
|
||||
pip install aiohttp requests py-gitea openai anthropic google-generativeai
|
||||
|
||||
- name: Run Code Review
|
||||
env:
|
||||
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
FULL_CONTEXT_MODEL: gpt-4o
|
||||
FULL_CONTEXT_MODEL: o3-mini
|
||||
FULL_CONTEXT_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
SINGLE_CHUNK_MODEL: gpt-4o
|
||||
SINGLE_CHUNK_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
SINGLE_CHUNK_MODEL: gemini-2.0-flash-exp
|
||||
SINGLE_CHUNK_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
EXCLUDE: "*.yml,*.yaml"
|
||||
run: python .gitea/scripts/code_review.py
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
|
||||
@@ -6,3 +6,5 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nalgebra = "0.33.2"
|
||||
approx = "0.5"
|
||||
20
src/context.rs
Normal file
20
src/context.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use crate::integrator::Integrator;
|
||||
use crate::platform::Platform;
|
||||
use crate::state::{State, StateType};
|
||||
use crate::system::System;
|
||||
use nalgebra::Vector3;
|
||||
|
||||
pub struct Context {
|
||||
pub system: System,
|
||||
pub integrator: Box<dyn Integrator>,
|
||||
pub platform: Box<dyn Platform>,
|
||||
}
|
||||
impl Context {
|
||||
pub fn set_positions(&mut self, _positions: Vec<Vector3<f32>>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_state(&self, _state_type: StateType) -> State {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
179
src/force/harmonic_angle_force.rs
Normal file
179
src/force/harmonic_angle_force.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
use crate::context::Context;
|
||||
use crate::force::Force;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct HarmonicAngleForce {}
|
||||
impl HarmonicAngleForce {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn add_angle(
|
||||
&mut self,
|
||||
_particle1: usize,
|
||||
_particle2: usize,
|
||||
_particle3: usize,
|
||||
_angle: f64,
|
||||
_k: f64,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_angle_parameters(
|
||||
&mut self,
|
||||
_angle_id: usize,
|
||||
_particle1: usize,
|
||||
_particle2: usize,
|
||||
_particle3: usize,
|
||||
_angle: f64,
|
||||
_k: f64,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Force for HarmonicAngleForce {
|
||||
fn update_parameters_in_context(&mut self, _context: &mut Context) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_periodic_boundary_conditions(&mut self, _status: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn use_periodic_boundary_conditions(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HarmonicAngleForce {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::HarmonicAngleForce;
|
||||
use crate::context::Context;
|
||||
use crate::force::Force;
|
||||
use crate::integrator::VerletIntegrator;
|
||||
use crate::platform::CPU;
|
||||
use crate::state::StateType;
|
||||
use crate::system::System;
|
||||
use approx::assert_relative_eq;
|
||||
use nalgebra::Vector3;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_harmonic_angle_force() {
|
||||
let mut system = System::new();
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
let integrator = VerletIntegrator { dt: 0.01 };
|
||||
|
||||
let mut forcefield = HarmonicAngleForce::new();
|
||||
forcefield.add_angle(0, 1, 2, PI / 3.0, 1.1);
|
||||
forcefield.add_angle(1, 2, 3, PI / 2.0, 1.2);
|
||||
|
||||
system.add_force(Box::new(forcefield.clone()));
|
||||
assert_eq!(forcefield.use_periodic_boundary_conditions(), false);
|
||||
assert_eq!(system.use_periodic_boundary_conditions(), false);
|
||||
|
||||
let mut context = Context {
|
||||
system,
|
||||
integrator: Box::new(integrator),
|
||||
platform: Box::new(CPU),
|
||||
};
|
||||
|
||||
let mut positions = vec![Vector3::zeros(); 4];
|
||||
positions[0] = Vector3::new(0.0, 2.0, 0.0);
|
||||
positions[1] = Vector3::new(0.0, 0.0, 0.0);
|
||||
positions[2] = Vector3::new(1.0, 0.0, 0.0);
|
||||
positions[3] = Vector3::new(2.0, 1.0, 0.0);
|
||||
|
||||
context.set_positions(positions);
|
||||
let state = context.get_state(StateType::Either);
|
||||
let forces = state.get_forces();
|
||||
let torque1 = 1.1 * PI / 6.0;
|
||||
let torque2 = 1.2 * PI / 4.0;
|
||||
assert_relative_eq!(forces[0], Vector3::new(torque1, 0.0, 0.0));
|
||||
assert_relative_eq!(forces[3], Vector3::new(-0.5 * torque2, 0.5 * torque2, 0.0));
|
||||
assert_relative_eq!(forces[1], -forces[0] - forces[2]);
|
||||
assert_relative_eq!(
|
||||
state.get_potential_energy(),
|
||||
0.5 * 1.1 * PI / 6.0 * PI / 6.0 + 0.5 * 1.2 * PI / 4.0 * PI / 4.0
|
||||
);
|
||||
|
||||
forcefield.set_angle_parameters(0, 0, 1, 2, PI / 3.1, 1.3);
|
||||
forcefield.set_angle_parameters(1, 1, 2, 3, PI / 2.1, 1.4);
|
||||
forcefield.update_parameters_in_context(&mut context);
|
||||
|
||||
let state = context.get_state(StateType::Either);
|
||||
let forces = state.get_forces();
|
||||
let dtheta1 = (PI / 2.0) - (PI / 3.1);
|
||||
let dtheta2 = (3.0 * PI / 4.0) - (PI / 2.1);
|
||||
let torque1 = 1.3 * dtheta1;
|
||||
let torque2 = 1.4 * dtheta2;
|
||||
assert_relative_eq!(forces[0], Vector3::new(torque1, 0.0, 0.0));
|
||||
assert_relative_eq!(forces[3], Vector3::new(-0.5 * torque2, 0.5 * torque2, 0.0));
|
||||
assert_relative_eq!(forces[1], -forces[0] - forces[2]);
|
||||
assert_relative_eq!(
|
||||
state.get_potential_energy(),
|
||||
0.5 * 1.3 * dtheta1 * dtheta1 + 0.5 * 1.4 * dtheta2 * dtheta2
|
||||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_harmonic_angle_force_with_periodic_boundary_conditions() {
|
||||
let mut system = System {};
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
system.set_default_periodic_box_vectors(
|
||||
Vector3::new(3.0, 0.0, 0.0),
|
||||
Vector3::new(0.0, 1.5, 0.0),
|
||||
Vector3::new(0.0, 0.0, 3.0),
|
||||
);
|
||||
|
||||
let integrator = VerletIntegrator { dt: 0.01 };
|
||||
let mut forcefield = HarmonicAngleForce {};
|
||||
forcefield.add_angle(0, 1, 2, PI / 3.0, 1.1);
|
||||
forcefield.set_periodic_boundary_conditions(true);
|
||||
|
||||
system.add_force(Box::new(forcefield));
|
||||
|
||||
let mut context = Context {
|
||||
system,
|
||||
integrator: Box::new(integrator),
|
||||
platform: Box::new(CPU),
|
||||
};
|
||||
|
||||
let mut positions = vec![Vector3::zeros(); 3];
|
||||
positions[0] = Vector3::new(0.0, 1.0, 0.0);
|
||||
positions[1] = Vector3::new(0.0, 0.0, 0.0);
|
||||
positions[2] = Vector3::new(1.0, 0.0, 0.0);
|
||||
context.set_positions(positions);
|
||||
|
||||
let state = context.get_state(StateType::Either);
|
||||
let forces = state.get_forces();
|
||||
|
||||
let torque = 1.1 * PI / 6.0;
|
||||
assert_relative_eq!(forces[0], Vector3::new(2.0 * torque, 0.0, 0.0));
|
||||
assert_relative_eq!(forces[2], Vector3::new(0.0, -torque, 0.0));
|
||||
|
||||
let energy = state.get_potential_energy();
|
||||
assert_relative_eq!(energy, 0.5 * 1.1 * (PI / 6.0) * (PI / 6.0));
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_harmonic_angle_force_parallel_computation() {
|
||||
// TODO: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
151
src/force/harmonic_bond_force.rs
Normal file
151
src/force/harmonic_bond_force.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use crate::context::Context;
|
||||
use crate::force::Force;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct HarmonicBondForce {}
|
||||
impl HarmonicBondForce {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn add_bond(&mut self, _particle1: usize, _particle2: usize, _length: f64, _k: f64) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_bond_parameters(
|
||||
&mut self,
|
||||
_particle1: usize,
|
||||
_particle2: usize,
|
||||
_length: f64,
|
||||
_k: f64,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl Force for HarmonicBondForce {
|
||||
fn update_parameters_in_context(&mut self, _context: &mut Context) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_periodic_boundary_conditions(&mut self, _status: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn use_periodic_boundary_conditions(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HarmonicBondForce {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::HarmonicBondForce;
|
||||
use crate::context::Context;
|
||||
use crate::force::Force;
|
||||
use crate::integrator::VerletIntegrator;
|
||||
use crate::platform::CPU;
|
||||
use crate::state::StateType;
|
||||
use crate::system::System;
|
||||
use approx::assert_relative_eq;
|
||||
use nalgebra::Vector3;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_harmonic_bond_force() {
|
||||
let mut system = System {};
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
let integrator = VerletIntegrator { dt: 0.01 };
|
||||
|
||||
let mut forcefield = HarmonicBondForce {};
|
||||
forcefield.add_bond(0, 1, 1.5, 0.8);
|
||||
forcefield.add_bond(1, 2, 1.2, 0.7);
|
||||
|
||||
system.add_force(Box::new(forcefield.clone()));
|
||||
|
||||
let mut context = Context {
|
||||
system,
|
||||
integrator: Box::new(integrator),
|
||||
platform: Box::new(CPU),
|
||||
};
|
||||
let mut positions = vec![Vector3::zeros(); 3];
|
||||
positions[0] = Vector3::new(0.0, 2.0, 0.0);
|
||||
positions[1] = Vector3::new(0.0, 0.0, 0.0);
|
||||
positions[2] = Vector3::new(1.0, 0.0, 0.0);
|
||||
context.set_positions(positions);
|
||||
|
||||
let state = context.get_state(StateType::Either);
|
||||
let forces = state.get_forces();
|
||||
assert_relative_eq!(forces[0], Vector3::new(0.0, -0.8 * 0.5, 0.0));
|
||||
assert_relative_eq!(forces[2], Vector3::new(0.7 * 0.2, 0.0, 0.0));
|
||||
assert_relative_eq!(forces[1], -forces[0] - forces[2]);
|
||||
|
||||
let energy = state.get_potential_energy();
|
||||
assert_relative_eq!(energy, 0.5 * 0.8 * 0.5 * 0.5 + 0.5 * 0.7 * 0.2 * 0.2);
|
||||
|
||||
forcefield.set_bond_parameters(0, 0, 1.6, 0.9);
|
||||
forcefield.set_bond_parameters(1, 1, 1.3, 0.8);
|
||||
forcefield.update_parameters_in_context(&mut context);
|
||||
|
||||
let state = context.get_state(StateType::Both);
|
||||
let forces = state.get_forces();
|
||||
assert_relative_eq!(forces[0], Vector3::new(0.0, -0.9 * 0.4, 0.0));
|
||||
assert_relative_eq!(forces[2], Vector3::new(0.8 * 0.3, 0.0, 0.0));
|
||||
assert_relative_eq!(forces[1], -forces[0] - forces[2]);
|
||||
|
||||
let energy = state.get_potential_energy();
|
||||
assert_relative_eq!(energy, 0.5 * 0.9 * 0.4 * 0.4 + 0.5 * 0.8 * 0.3 * 0.3);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_harmonic_bond_force_with_periodic_boundary_conditions() {
|
||||
let mut system = System {};
|
||||
system.add_particle(1.0);
|
||||
system.add_particle(1.0);
|
||||
system.set_default_periodic_box_vectors(
|
||||
Vector3::new(3.0, 0.0, 0.0),
|
||||
Vector3::new(0.0, 3.0, 0.0),
|
||||
Vector3::new(0.0, 0.0, 3.0),
|
||||
);
|
||||
|
||||
let integrator = VerletIntegrator { dt: 0.01 };
|
||||
let mut forcefield = HarmonicBondForce {};
|
||||
forcefield.add_bond(0, 1, 1.2, 0.8);
|
||||
forcefield.set_periodic_boundary_conditions(true);
|
||||
|
||||
system.add_force(Box::new(forcefield));
|
||||
|
||||
let mut context = Context {
|
||||
system,
|
||||
integrator: Box::new(integrator),
|
||||
platform: Box::new(CPU),
|
||||
};
|
||||
|
||||
let mut positions = vec![Vector3::zeros(); 2];
|
||||
positions[0] = Vector3::new(0.0, 2.0, 0.0);
|
||||
positions[1] = Vector3::new(0.0, 0.0, 0.0);
|
||||
context.set_positions(positions);
|
||||
|
||||
let state = context.get_state(StateType::Both);
|
||||
let forces = state.get_forces();
|
||||
assert_relative_eq!(forces[0], Vector3::new(0.0, -0.8 * 0.2, 0.0));
|
||||
assert_relative_eq!(forces[1], Vector3::new(0.0, 0.8 * 0.2, 0.0));
|
||||
|
||||
let energy = state.get_potential_energy();
|
||||
assert_relative_eq!(energy, 0.5 * 0.8 * 0.2 * 0.2);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_harmonic_bond_force_parallel_computation() {
|
||||
// TODO: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
15
src/force/mod.rs
Normal file
15
src/force/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::context::Context;
|
||||
|
||||
pub trait Force: Debug {
|
||||
fn update_parameters_in_context(&mut self, context: &mut Context);
|
||||
fn set_periodic_boundary_conditions(&mut self, status: bool);
|
||||
fn use_periodic_boundary_conditions(&self) -> bool;
|
||||
}
|
||||
|
||||
pub mod harmonic_angle_force;
|
||||
pub use harmonic_angle_force::HarmonicAngleForce;
|
||||
|
||||
pub mod harmonic_bond_force;
|
||||
pub use harmonic_bond_force::HarmonicBondForce;
|
||||
6
src/integrator/mod.rs
Normal file
6
src/integrator/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
pub trait Integrator {}
|
||||
|
||||
pub struct VerletIntegrator {
|
||||
pub dt: f64,
|
||||
}
|
||||
impl Integrator for VerletIntegrator {}
|
||||
233
src/lib.rs
233
src/lib.rs
@@ -1,227 +1,8 @@
|
||||
pub mod context;
|
||||
pub mod force;
|
||||
pub mod platform;
|
||||
pub mod state;
|
||||
pub mod system;
|
||||
|
||||
|
||||
|
||||
pub mod system{
|
||||
use crate::force::Force;
|
||||
use crate::virtual_site::VirtualSite;
|
||||
|
||||
pub struct System{}
|
||||
impl System{
|
||||
pub fn add_particle(&mut self, mass: f32){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_num_particles(&self) -> usize{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_particle_mass(&self, index: usize) -> f32{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_particle_mass(&mut self, index: usize, mass: f32){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn is_virtual_site(&self, index: usize) -> bool{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_virtual_site(&self, index: usize) -> Option<Box<dyn VirtualSite>>{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_virtual_site(&mut self, index: usize, vsite: Option<Box<dyn VirtualSite>>){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn add_constraint(&mut self, p1: usize, p2: usize, dist: f32){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn remove_constraint(&mut self, index: usize){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_num_constraints(&self) -> usize{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_constraint_parameters(&self, index: usize) -> (usize, usize, f32){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_constraint_parameters(&mut self, index: usize, p1: usize, p2: usize, dist: f32){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn add_force(&mut self, force: Box<dyn Force>){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn remove_force(&mut self, index: usize){
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_num_forces(&self) -> usize{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_force(&self, index: usize) -> Box<dyn Force>{
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod force{
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Force : Debug{}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct HarmonicAngleForce{}
|
||||
impl Force for HarmonicAngleForce{}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct HarmonicBondForce{}
|
||||
impl Force for HarmonicBondForce{}
|
||||
}
|
||||
|
||||
pub mod virtual_site{
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait VirtualSite : Debug{}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TwoParticleAverageSite{
|
||||
pub p1: usize,
|
||||
pub p2: usize,
|
||||
pub weight1: f32,
|
||||
pub weight2: f32,
|
||||
}
|
||||
impl TwoParticleAverageSite{
|
||||
pub fn new(p1: usize, p2: usize, weight1: f32, weight2: f32) -> Self{
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl VirtualSite for TwoParticleAverageSite{}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use crate::system::System;
|
||||
use crate::force::{HarmonicAngleForce, HarmonicBondForce};
|
||||
use crate::virtual_site::TwoParticleAverageSite;
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_particles(){
|
||||
let num_particles = 10;
|
||||
let mut system = System{};
|
||||
|
||||
for i in 0..num_particles{
|
||||
system.add_particle(1.0 + 0.1 * i as f32);
|
||||
}
|
||||
system.set_particle_mass(5, 100.0);
|
||||
assert_eq!(system.get_num_particles(), num_particles);
|
||||
|
||||
for i in 0..num_particles{
|
||||
let expected_mass = if i == 5 { 100.0 } else { 1.0 + 0.1 * i as f32 };
|
||||
assert_eq!(system.get_particle_mass(i), expected_mass);
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_constraints(){
|
||||
let num_particles = 10;
|
||||
let mut system = System{};
|
||||
|
||||
for i in 0..num_particles - 1{
|
||||
system.add_constraint(i, i + 1, 0.2 * i as f32);
|
||||
}
|
||||
system.remove_constraint(5);
|
||||
system.set_constraint_parameters(3, 0, 5, 99.0);
|
||||
assert_eq!(system.get_num_constraints(), num_particles - 2);
|
||||
|
||||
for i in 0..num_particles - 2{
|
||||
let (p1, p2, dist) = system.get_constraint_parameters(i);
|
||||
if i == 3 {
|
||||
assert_eq!(p1, 0);
|
||||
assert_eq!(p2, 5);
|
||||
assert_eq!(dist, 99.0);
|
||||
} else {
|
||||
let j = if i < 5 { i } else { i + 1 };
|
||||
assert_eq!(p1, j);
|
||||
assert_eq!(p2, j + 1);
|
||||
assert_eq!(dist, 0.2 * j as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_force(){
|
||||
let mut system = System{};
|
||||
let bonds = HarmonicBondForce{};
|
||||
system.add_force(Box::new(bonds));
|
||||
|
||||
let angles = HarmonicAngleForce{};
|
||||
system.add_force(Box::new(angles));
|
||||
|
||||
assert_eq!(system.get_num_forces(), 2);
|
||||
// Todo: Rust-OpenMM
|
||||
// assert_eq!(system.get_force(0), Box::new(bonds));
|
||||
// assert_eq!(system.get_force(1), Box::new(angles));
|
||||
|
||||
system.remove_force(0);
|
||||
assert_eq!(system.get_num_forces(), 1);
|
||||
// Todo: Rust-OpenMM
|
||||
// assert_eq!(system.get_force(0), Box::new(angles));
|
||||
}
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_virtual_site(){
|
||||
let mut system = System{};
|
||||
let num_particles = 10;
|
||||
for i in 0..num_particles{
|
||||
system.add_particle(1.0);
|
||||
assert!(!system.is_virtual_site(i));
|
||||
}
|
||||
|
||||
let vsite = TwoParticleAverageSite::new(2, 3, 0.4, 0.6);
|
||||
system.set_virtual_site(4, Some(Box::new(vsite)));
|
||||
for i in 0..num_particles{
|
||||
assert_eq!(system.is_virtual_site(i), i == 4);
|
||||
}
|
||||
// Todo: Rust-OpenMM
|
||||
// assert_eq!(system.get_virtual_site(4), Some(Box::new(vsite)));
|
||||
system.set_virtual_site(4, None);
|
||||
for i in 0..num_particles{
|
||||
assert!(!system.is_virtual_site(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod integrator;
|
||||
pub mod virtual_site;
|
||||
|
||||
7
src/platform/mod.rs
Normal file
7
src/platform/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub trait Platform {}
|
||||
|
||||
pub struct CPU;
|
||||
pub struct OpenCL;
|
||||
|
||||
impl Platform for CPU {}
|
||||
impl Platform for OpenCL {}
|
||||
24
src/state.rs
Normal file
24
src/state.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use nalgebra::Vector3;
|
||||
|
||||
pub enum StateType {
|
||||
Both,
|
||||
Either,
|
||||
Forces,
|
||||
Energy,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub state_type: StateType,
|
||||
pub forces: Vec<Vector3<f64>>,
|
||||
pub energy: f64,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn get_forces(&self) -> &Vec<Vector3<f64>> {
|
||||
&self.forces
|
||||
}
|
||||
|
||||
pub fn get_potential_energy(&self) -> f64 {
|
||||
self.energy
|
||||
}
|
||||
}
|
||||
211
src/system.rs
Normal file
211
src/system.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use crate::force::Force;
|
||||
use crate::virtual_site::VirtualSite;
|
||||
use nalgebra::Vector3;
|
||||
pub struct System {}
|
||||
impl System {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn add_particle(&mut self, _mass: f32) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_num_particles(&self) -> usize {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_particle_mass(&self, _index: usize) -> f32 {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_particle_mass(&mut self, _index: usize, _mass: f32) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn is_virtual_site(&self, _index: usize) -> bool {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_virtual_site(&self, _index: usize) -> Option<Box<dyn VirtualSite>> {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_virtual_site(&mut self, _index: usize, _vsite: Option<Box<dyn VirtualSite>>) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn add_constraint(&mut self, _p1: usize, _p2: usize, _dist: f32) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn remove_constraint(&mut self, _index: usize) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_num_constraints(&self) -> usize {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_constraint_parameters(&self, _index: usize) -> (usize, usize, f32) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_constraint_parameters(&mut self, _index: usize, _p1: usize, _p2: usize, _dist: f32) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn add_force(&mut self, _force: Box<dyn Force>) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn remove_force(&mut self, _index: usize) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_num_forces(&self) -> usize {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_force(&self, _index: usize) -> Box<dyn Force> {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_default_periodic_box_vectors(
|
||||
&mut self,
|
||||
_a: Vector3<f32>,
|
||||
_b: Vector3<f32>,
|
||||
_c: Vector3<f32>,
|
||||
) {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn use_periodic_boundary_conditions(&self) -> bool {
|
||||
// Todo: Rust-OpenMM
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for System {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::force::{HarmonicAngleForce, HarmonicBondForce};
|
||||
use crate::system::System;
|
||||
use crate::virtual_site::TwoParticleAverageSite;
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_particles() {
|
||||
let num_particles = 10;
|
||||
let mut system = System {};
|
||||
|
||||
for i in 0..num_particles {
|
||||
system.add_particle(1.0 + 0.1 * i as f32);
|
||||
}
|
||||
system.set_particle_mass(5, 100.0);
|
||||
assert_eq!(system.get_num_particles(), num_particles);
|
||||
|
||||
for i in 0..num_particles {
|
||||
let expected_mass = if i == 5 { 100.0 } else { 1.0 + 0.1 * i as f32 };
|
||||
assert_eq!(system.get_particle_mass(i), expected_mass);
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_constraints() {
|
||||
let num_particles = 10;
|
||||
let mut system = System {};
|
||||
|
||||
for i in 0..num_particles - 1 {
|
||||
system.add_constraint(i, i + 1, 0.2 * i as f32);
|
||||
}
|
||||
system.remove_constraint(5);
|
||||
system.set_constraint_parameters(3, 0, 5, 99.0);
|
||||
assert_eq!(system.get_num_constraints(), num_particles - 2);
|
||||
|
||||
for i in 0..num_particles - 2 {
|
||||
let (p1, p2, dist) = system.get_constraint_parameters(i);
|
||||
if i == 3 {
|
||||
assert_eq!(p1, 0);
|
||||
assert_eq!(p2, 5);
|
||||
assert_eq!(dist, 99.0);
|
||||
} else {
|
||||
let j = if i < 5 { i } else { i + 1 };
|
||||
assert_eq!(p1, j);
|
||||
assert_eq!(p2, j + 1);
|
||||
assert_eq!(dist, 0.2 * j as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_force() {
|
||||
let mut system = System {};
|
||||
let bonds = HarmonicBondForce {};
|
||||
system.add_force(Box::new(bonds));
|
||||
|
||||
let angles = HarmonicAngleForce {};
|
||||
system.add_force(Box::new(angles));
|
||||
|
||||
assert_eq!(system.get_num_forces(), 2);
|
||||
// Todo: Rust-OpenMM
|
||||
// assert_eq!(system.get_force(0), Box::new(bonds));
|
||||
// assert_eq!(system.get_force(1), Box::new(angles));
|
||||
|
||||
system.remove_force(0);
|
||||
assert_eq!(system.get_num_forces(), 1);
|
||||
// Todo: Rust-OpenMM
|
||||
// assert_eq!(system.get_force(0), Box::new(angles));
|
||||
}
|
||||
|
||||
// Todo: Rust-OpenMM
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_system_virtual_site() {
|
||||
let mut system = System {};
|
||||
let num_particles = 10;
|
||||
for i in 0..num_particles {
|
||||
system.add_particle(1.0);
|
||||
assert!(!system.is_virtual_site(i));
|
||||
}
|
||||
|
||||
let vsite = TwoParticleAverageSite::new(2, 3, 0.4, 0.6);
|
||||
system.set_virtual_site(4, Some(Box::new(vsite)));
|
||||
for i in 0..num_particles {
|
||||
assert_eq!(system.is_virtual_site(i), i == 4);
|
||||
}
|
||||
// Todo: Rust-OpenMM
|
||||
// assert_eq!(system.get_virtual_site(4), Some(Box::new(vsite)));
|
||||
system.set_virtual_site(4, None);
|
||||
for i in 0..num_particles {
|
||||
assert!(!system.is_virtual_site(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/virtual_site.rs
Normal file
17
src/virtual_site.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait VirtualSite: Debug {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TwoParticleAverageSite {
|
||||
pub p1: usize,
|
||||
pub p2: usize,
|
||||
pub weight1: f32,
|
||||
pub weight2: f32,
|
||||
}
|
||||
impl TwoParticleAverageSite {
|
||||
pub fn new(_p1: usize, _p2: usize, _weight1: f32, _weight2: f32) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl VirtualSite for TwoParticleAverageSite {}
|
||||
Reference in New Issue
Block a user