diff --git a/README.md b/README.md index 8c03722..5db18f2 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ As the main goal of this library is to allow access to the atomical data many me | ANISOU | ✔️ | ✔️ | atom_site | | SCALE | ✔️ | ✔️ | _atom_sites.Cartn_transf | | ORIGX | ✔️ | ✔️ | _database_PDB_matrix.origx | -| MATRIX | ✔️ | ❌ | ? | +| MATRIX | ✔️ | ✔️ | struct_ncs_oper | | CRYSTAL | ✔️ | ✔️ | cell + symmetry | | MODEL | ✔️ | ✔️ | atom_site | | MASTER | 〰️ | ❌ | _pdbx_database_PDB_master | diff --git a/src/read/mmcif/parser.rs b/src/read/mmcif/parser.rs index 5fd702d..5a41c18 100644 --- a/src/read/mmcif/parser.rs +++ b/src/read/mmcif/parser.rs @@ -60,6 +60,7 @@ fn parse_mmcif( let mut pdb = PDB::default(); let mut errors: Vec = Vec::new(); let mut unit_cell = UnitCell::default(); + let mut mtrix_id = None; pdb.identifier = Some(input.name.clone()); @@ -140,6 +141,59 @@ fn parse_mmcif( #[allow(clippy::unwrap_used)] parse_matrix(s, get_f64(&single.content, &context),pdb.origx.as_mut().unwrap(), &context) } + s if s.starts_with("structs_ncs_oper.") => { + if s.ends_with("id") { + match get_usize(&single.content, &context) { + Err(e) => Some(e), + Ok(Some(id)) => { + mtrix_id = Some(id); + pdb.add_mtrix(MtriX::new(id, TransformationMatrix::identity(), true)); + None + } + Ok(None) => Some(PDBError::new( + ErrorLevel::InvalidatingError, + "MtriX with missing ID", + "If a MtriX id is given it should be a number not a missing value.", + context.clone(), + )) + } + } else { + #[allow(clippy::unwrap_used)] + match mtrix_id { + Some(id) => { + let mut mtrix = pdb.mtrix_mut().find(|m| m.serial_number == id).unwrap(); + if s.ends_with("code") { + match get_text(&single.content, &context) { + Ok(Some(t)) if t == "given" => {mtrix.contained = true; None}, + Ok(Some(t)) if t == "generate" => {mtrix.contained = false; None}, + Ok(Some(_)) => Some(PDBError::new( + ErrorLevel::InvalidatingError, + "MtriX code invalid", + "Only the values 'generate' and 'given' are valid for `_struct_ncs_oper.code`.", + context.clone(), + )), + _ => Some(PDBError::new( + ErrorLevel::InvalidatingError, + "MtriX code invalid", + "The value for `_struct_ncs_oper.code` should be a textual value.", + context.clone(), + )), + } + } else if s.ends_with("details") { + None // Ignore the details, it will not be saved somewhere + } else { + parse_matrix(s, get_f64(&single.content, &context),&mut mtrix.transformation, &context) + } + }, + None => Some(PDBError::new( + ErrorLevel::InvalidatingError, + "MtriX matrix given without ID", + "The MtriX ID (`_struct_ncs_oper.id`) should be given before any matrix information is given.", + context.clone(), + )) + } + } + } _ => None, } .map(|e| vec![e]) diff --git a/src/save/mmcif.rs b/src/save/mmcif.rs index 79bc019..bb1c16a 100644 --- a/src/save/mmcif.rs +++ b/src/save/mmcif.rs @@ -104,7 +104,8 @@ _cell.Z_PDB {}", if let Some(scale) = &pdb.scale { let ma = scale.matrix(); write!( - "_atom_sites.entry_id '{}' + "# Scale definition + _atom_sites.entry_id '{}' _atom_sites.Cartn_transf_matrix[1][1] {} _atom_sites.Cartn_transf_matrix[1][2] {} _atom_sites.Cartn_transf_matrix[1][3] {} @@ -137,7 +138,8 @@ _atom_sites.Cartn_transf_vector[3] {}", if let Some(origx) = &pdb.origx { let ma = origx.matrix(); write!( - "_database_PDB_matrix.entry_id '{}' + "# OrigX definition +_database_PDB_matrix.entry_id '{}' _database_PDB_matrix.origx[1][1] {} _database_PDB_matrix.origx[1][2] {} _database_PDB_matrix.origx[1][3] {} @@ -166,6 +168,46 @@ _database_PDB_matrix.origx_vector[3] {}", ); } + // MtriX + for mtrix in pdb.mtrix() { + let ma = mtrix.transformation.matrix(); + write!( + r#"# OrigX definition +_struct_ncs_oper.id '{}' +_struct_ncs_oper.code {} +_struct_ncs_oper.matrix[1][1] {} +_struct_ncs_oper.matrix[1][2] {} +_struct_ncs_oper.matrix[1][3] {} +_struct_ncs_oper.matrix[2][1] {} +_struct_ncs_oper.matrix[2][2] {} +_struct_ncs_oper.matrix[2][3] {} +_struct_ncs_oper.matrix[3][1] {} +_struct_ncs_oper.matrix[3][2] {} +_struct_ncs_oper.matrix[3][3] {} +_struct_ncs_oper.vector[1] {} +_struct_ncs_oper.vector[2] {} +_struct_ncs_oper.vector[3] {}"#, + mtrix.serial_number, + if mtrix.contained { + "given" + } else { + "generated" + }, + ma[0][0], + ma[0][1], + ma[0][2], + ma[1][0], + ma[1][1], + ma[1][2], + ma[2][0], + ma[2][1], + ma[2][2], + ma[0][3], + ma[1][3], + ma[2][3], + ); + } + if let Some(symmetry) = &pdb.symmetry { write!( "# Space group definition